def _eval_apply(cls, arg): arg = Basic.sympify(arg) if isinstance(arg, Basic.Number): if isinstance(arg, Basic.NaN): return S.NaN elif isinstance(arg, Basic.Infinity): return S.Infinity elif isinstance(arg, Basic.Integer): if arg.is_positive: return Basic.Factorial(arg-1) else: return S.ComplexInfinity elif isinstance(arg, Basic.Rational): if arg.q == 2: n = abs(arg.p) / arg.q if arg.is_positive: k, coeff = n, S.One else: n = k = n + 1 if n & 1 == 0: coeff = S.One else: coeff = S.NegativeOne for i in range(3, 2*k, 2): coeff *= i if arg.is_positive: return coeff*Basic.sqrt(S.Pi) / 2**n else: return 2**n*Basic.sqrt(S.Pi) / coeff
def __new__(cls, f, z, **assumptions): f = Basic.sympify(f) if not f.is_polynomial(z): return f obj = Basic.__new__(cls, **assumptions) obj._args = (f.as_polynomial(z), z) return obj
def separate(expr, deep=False): """Rewrite or separate a power of product to a product of powers but without any expanding, ie. rewriting products to summations. >>> from sympy import * >>> x, y, z = symbols('x', 'y', 'z') >>> separate((x*y)**2) x**2*y**2 >>> separate((x*(y*z)**3)**2) x**2*y**6*z**6 >>> separate((x*sin(x))**y + (x*cos(x))**y) x**y*cos(x)**y + x**y*sin(x)**y #>>> separate((exp(x)*exp(y))**x) #exp(x*y)*exp(x**2) Notice that summations are left un touched. If this is not the requested behaviour, apply 'expand' to input expression before: >>> separate(((x+y)*z)**2) z**2*(x + y)**2 >>> separate((x*y)**(1+z)) x**(1 + z)*y**(1 + z) """ expr = Basic.sympify(expr) if isinstance(expr, Basic.Pow): terms, expo = [], separate(expr.exp, deep) #print expr, terms, expo, expr.base if isinstance(expr.base, Mul): t = [ separate(Basic.Pow(t,expo), deep) for t in expr.base ] return Basic.Mul(*t) elif isinstance(expr.base, Basic.exp): if deep == True: return Basic.exp(separate(expr.base[0], deep)*expo) else: return Basic.exp(expr.base[0]*expo) else: return Basic.Pow(separate(expr.base, deep), expo) elif isinstance(expr, (Basic.Add, Basic.Mul)): return type(expr)(*[ separate(t, deep) for t in expr ]) elif isinstance(expr, Basic.Function) and deep: return expr.func(*[ separate(t) for t in expr]) else: return expr
def __new__(cls, f, *symbols, **assumptions): f = Basic.sympify(f) if isinstance(f, Basic.Number): if isinstance(f, Basic.NaN): return S.NaN elif isinstance(f, Basic.Zero): return S.Zero if not symbols: limits = f.atoms(Symbol) if not limits: return f else: limits = [] for V in symbols: if isinstance(V, Symbol): limits.append(V) continue elif isinstance(V, Equality): if isinstance(V.lhs, Symbol): if isinstance(V.rhs, Interval): limits.append((V.lhs, V.rhs.start, V.rhs.end)) else: limits.append((V.lhs, V.rhs)) continue elif isinstance(V, (tuple, list)): if len(V) == 1: if isinstance(V[0], Symbol): limits.append(V[0]) continue elif len(V) in (2, 3): if isinstance(V[0], Symbol): limits.append(tuple(V)) continue raise ValueError("Invalid summation variable or limits") obj = Basic.__new__(cls, **assumptions) obj._args = (f, tuple(limits)) return obj
def __new__(cls, function, *symbols, **assumptions): function = Basic.sympify(function) if isinstance(function, Basic.Number): if isinstance(function, Basic.NaN): return S.NaN elif isinstance(function, Basic.Infinity): return S.Infinity elif isinstance(function, Basic.NegativeInfinity): return S.NegativeInfinity if symbols: limits = [] for V in symbols: if isinstance(V, Symbol): limits.append((V,None)) continue elif isinstance(V, (tuple, list)): if len(V) == 3: limits.append( (V[0],tuple(V[1:])) ) continue elif len(V) == 1: if isinstance(V[0], Symbol): limits.append((V[0],None)) continue raise ValueError("Invalid integration variable or limits") else: limits = func.atoms(Symbol) if not limits: return function obj = Basic.__new__(cls, **assumptions) obj._args = (function, tuple(limits)) return obj
def __new__(cls, term, *symbols, **assumptions): term = Basic.sympify(term) if isinstance(term, Basic.Number): if isinstance(term, Basic.NaN): return S.NaN elif isinstance(term, Basic.Infinity): return S.NaN elif isinstance(term, Basic.NegativeInfinity): return S.NaN elif isinstance(term, Basic.Zero): return S.Zero elif isinstance(term, Basic.One): return S.One if len(symbols) == 1: symbol = symbols[0] if isinstance(symbol, Basic.Equality): k = symbol.lhs a = symbol.rhs.start n = symbol.rhs.end elif isinstance(symbol, (tuple, list)): k, a, n = symbol else: raise ValueError("Invalid arguments") k, a, n = map(Basic.sympify, (k, a, n)) if isinstance(a, Basic.Number) and isinstance(n, Basic.Number): return Mul(*[term.subs(k, i) for i in xrange(int(a), int(n) + 1)]) else: raise NotImplementedError obj = Basic.__new__(cls, **assumptions) obj._args = (term, k, a, n) return obj
def hypersimp(term, n, consecutive=True, simplify=True): """Given combinatorial term a(n) simplify its consecutive term ratio ie. a(n+1)/a(n). The term can be composed of functions and integer sequences which have equivalent represenation in terms of gamma special function. Currently ths includes factorials (falling, rising), binomials and gamma it self. The algorithm performs three basic steps: (1) Rewrite all functions in terms of gamma, if possible. (2) Rewrite all occurences of gamma in terms of produtcs of gamma and rising factorial with integer, absolute constant exponent. (3) Perform simplification of nested fractions, powers and if the resulting expression is a quotient of polynomials, reduce their total degree. If the term given is hypergeometric then the result of this procudure is a quotient of polynomials of minimal degree. Sequence is hypergeometric if it is anihilated by linear, homogeneous recurrence operator of first order, so in other words when a(n+1)/a(n) is a rational function. When the status of being hypergeometric or not, is required then you can avoid additional simplification by unsetting 'simplify' flag. This algorithm, due to Wolfram Koepf, is very simple but powerful, however its full potential will be visible when simplification in general will improve. For more information on the implemented algorithm refer to: [1] W. Koepf, Algorithms for m-fold Hypergeometric Summation, Journal of Symbolic Computation (1995) 20, 399-417 """ term = Basic.sympify(term) if consecutive == True: term = term.subs(n, n+1)/term expr = term.rewrite(gamma).expand(func=True, basic=False) p, q = together(expr).as_numer_denom() if p.is_polynomial(n) and q.is_polynomial(n): if simplify == True: from sympy.polynomials import gcd, quo G = gcd(p, q, n) if not isinstance(G, Basic.One): p = quo(p, G, n) q = quo(q, G, n) p = p.as_polynomial(n) q = q.as_polynomial(n) a, p = p.as_integer() b, q = q.as_integer() p = p.as_basic() q = q.as_basic() return (b/a) * (p/q) return p/q else: return None
def fraction(expr, exact=False): """Returns a pair with expression's numerator and denominator. If the given expression is not a fraction then this function will assume that the denominator is equal to one. This function will not make any attempt to simplify nested fractions or to do any term rewriting at all. If only one of the numerator/denominator pair is needed then use numer(expr) or denom(expr) functions respectively. >>> from sympy import * >>> x, y = symbols('x', 'y') >>> fraction(x/y) (x, y) >>> fraction(x) (x, 1) >>> fraction(1/y**2) (1, y**2) >>> fraction(x*y/2) (x*y, 2) >>> fraction(Rational(1, 2)) (1, 2) This function will also work fine with assumptions: >>> k = Symbol('k', negative=True) >>> fraction(x * y**k) (x, y**(-k)) If we know nothing about sign of some exponent and 'exact' flag is unset, then structure this exponent's structure will be analyzed and pretty fraction will be returned: >>> fraction(2*x**(-y)) (2, x**y) #>>> fraction(exp(-x)) #(1, exp(x)) >>> fraction(exp(-x), exact=True) (exp(-x), 1) """ expr = Basic.sympify(expr) #XXX this only works sometimes (caching bug?) if expr == exp(-Symbol("x")) and exact: return (expr, 1) numer, denom = [], [] for term in make_list(expr, Mul): if isinstance(term, Pow): if term.exp.is_negative: if term.exp == Integer(-1): denom.append(term.base) else: denom.append(Pow(term.base, -term.exp)) elif not exact and isinstance(term.exp, Mul): coeff, tail = term.exp[0], Mul(*term.exp[1:])#term.exp.getab() if isinstance(coeff, Rational) and coeff.is_negative: denom.append(Pow(term.base, -term.exp)) else: numer.append(term) else: numer.append(term) elif isinstance(term, Basic.exp): if term[0].is_negative: denom.append(Basic.exp(-term[0])) elif not exact and isinstance(term[0], Mul): coeff, tail = term[0], Mul(*term[1:])#term.args.getab() if isinstance(coeff, Rational) and coeff.is_negative: denom.append(Basic.exp(-term[0])) else: numer.append(term) else: numer.append(term) elif isinstance(term, Rational): if term.is_integer: numer.append(term) else: numer.append(Rational(term.p)) denom.append(Rational(term.q)) else: numer.append(term) return Mul(*numer), Mul(*denom)
def collect(expr, syms, evaluate=True, exact=False): """Collect additive terms with respect to a list of symbols up to powers with rational exponents. By the term symbol here are meant arbitrary expressions, which can contain powers, products, sums etc. In other words symbol is a pattern which will be searched for in the expression's terms. This function will not apply any redundant expanding to the input expression, so user is assumed to enter expression in final form. This makes 'collect' more predictable as there is no magic behind the scenes. However it is important to note, that powers of products are converted to products of powers using 'separate' function. There are two possible types of output. First, if 'evaluate' flag is set, this function will return a single expression or else it will return a dictionary with separated symbols up to rational powers as keys and collected sub-expressions as values respectively. >>> from sympy import * >>> x, y, z = symbols('x', 'y', 'z') >>> a, b, c = symbols('a', 'b', 'c') This function can collect symbolic coefficients in polynomial or rational expressions. It will manage to find all integer or rational powers of collection variable: >>> collect(a*x**2 + b*x**2 + a*x - b*x + c, x) c + x*(a - b) + x**2*(a + b) The same result can achieved in dictionary form: >>> collect(a*x**2 + b*x**2 + a*x - b*x + c, x, evaluate=False) {1: c, x**2: a + b, x: a - b} You can also work with multi-variate polynomials. However remember that this function is greedy so it will care only about a single symbol at time, in specification order: >>> collect(x**2 + y*x**2 + x*y + y + a*y, [x, y]) x*y + y*(1 + a) + x**2*(1 + y) >>> collect(x**2*y**4 + z*(x*y**2)**2 + z + a*z, [x*y**2, z]) z*(1 + a) + x**2*y**4*(1 + z) Also more complicated expressions can be used as patterns: >>> collect(a*sin(2*x) + b*sin(2*x), sin(2*x)) (a + b)*sin(2*x) >>> collect(a*x**2*log(x)**2 + b*(x*log(x))**2, x*log(x)) x**2*log(x)**2*(a + b) It is also possible to work with symbolic powers, although it has more complicated behaviour, because in this case power's base and symbolic part of the exponent are treated as a single symbol: #>>> collect(a*x**c + b*x**c, x) #a*x**c + b*x**c #>>> collect(a*x**c + b*x**c, x**c) #x**c*(a + b) However if you incorporate rationals to the exponents, then you will get well known behaviour: #>>> collect(a*x**(2*c) + b*x**(2*c), x**c) #x**(2*c)*(a + b) Note also that all previously stated facts about 'collect' function apply to the exponential function, so you can get: #>>> collect(a*exp(2*x) + b*exp(2*x), exp(x)) #(a+b)*exp(2*x) If you are interested only in collecting specific powers of some symbols then set 'exact' flag in arguments: >>> collect(a*x**7 + b*x**7, x, exact=True) a*x**7 + b*x**7 >>> collect(a*x**7 + b*x**7, x**7, exact=True) x**7*(a + b) You can also apply this function to differential equations, where derivatives of arbitary order can be collected: #>>> from sympy import Derivative as D #>>> f = Function(x) #>>> collect(a*D(f,x) + b*D(f,x), D(f,x)) #(a+b)*Function'(x) #>>> collect(a*D(D(f,x),x) + b*D(D(f,x),x), D(f,x)) #(a+b)*(Function'(x))' #>>> collect(a*D(D(f,x),x) + b*D(D(f,x),x), D(f,x), exact=True) #a*(Function'(x))'+b*(Function'(x))' #>>> collect(a*D(D(f,x),x) + b*D(D(f,x),x) + a*D(f,x) + b*D(f,x), D(f,x)) #(a+b)*Function'(x)+(a+b)*(Function'(x))' Or you can even match both derivative order and exponent at time: #>>> collect(a*D(D(f,x),x)**2 + b*D(D(f,x),x)**2, D(f,x)) #(a+b)*(Function'(x))'**2 """ def make_expression(terms): product = [] for term, rat, sym, deriv in terms: if deriv is not None: var, order = deriv while order > 0: term, order = Derivative(term, var), order-1 if sym is None: if isinstance(rat, Basic.One): product.append(term) else: product.append(Pow(term, rat)) else: product.append(Pow(term, rat*sym)) return Mul(*product) def parse_derivative(deriv): # scan derivatives tower in the input expression and return # underlying function and maximal differentiation order expr, sym, order = deriv.f, deriv.x, 1 while isinstance(expr, Derivative) and expr.x == sym: expr, order = expr.f, order+1 return expr, (sym, Rational(order)) def parse_term(expr): rat_expo, sym_expo = Rational(1), None sexpr, deriv = expr, None if isinstance(expr, Pow): if isinstance(expr.base, Derivative): sexpr, deriv = parse_derivative(expr.base) else: sexpr = expr.base if isinstance(expr.exp, Rational): rat_expo = expr.exp elif isinstance(expr.exp, Mul): coeff, tail = term.exp.as_coeff_terms() if isinstance(coeff, Rational): rat_expo, sym_expo = coeff, Basic.Mul(*tail) else: sym_expo = expr.exp else: sym_expo = expr.exp elif isinstance(expr, Basic.exp): if isinstance(expr[0], Rational): sexpr, rat_expo = Basic.exp(Rational(1)), expr[0] elif isinstance(expr[0], Mul): coeff, tail = expr[0].as_coeff_terms() if isinstance(coeff, Rational): sexpr, rat_expo = Basic.exp(Basic.Mul(*tail)), coeff elif isinstance(expr, Derivative): sexpr, deriv = parse_derivative(expr) return sexpr, rat_expo, sym_expo, deriv def parse_expression(terms, pattern): pattern = make_list(pattern, Mul) if len(terms) < len(pattern): # pattern is longer than matched product # so no chance for positive parsing result return None else: pattern = [ parse_term(elem) for elem in pattern ] elems, common_expo, has_deriv = [], Rational(1), False for elem, e_rat, e_sym, e_ord in pattern: if e_ord is not None: # there is derivative in the pattern so # there will by small performance penalty has_deriv = True for j in range(len(terms)): term, t_rat, t_sym, t_ord = terms[j] if elem == term and e_sym == t_sym: if exact == False: # we don't have to exact so find common exponent # for both expression's term and pattern's element expo = t_rat / e_rat if isinstance(common_expo, Basic.One): common_expo = expo else: # common exponent was negotiated before so # teher is no chance for pattern match unless # common and current exponents are equal if common_expo != expo: return None else: # we ought to be exact so all fields of # interest must match in very details if e_rat != t_rat or e_ord != t_ord: continue # found common term so remove it from the expression # and try to match next element in the pattern elems.append(terms[j]) del terms[j] break else: # pattern element not found return None return terms, elems, common_expo, has_deriv if evaluate: if isinstance(expr, Basic.Mul): ret = 1 for term in expr: ret *= collect(term, syms, True, exact) return ret elif isinstance(expr, Basic.Pow): b = collect(expr.base, syms, True, exact) return Basic.Pow(b, expr.exp) summa = [ separate(i) for i in make_list(Basic.sympify(expr), Add) ] if isinstance(syms, list): syms = [ separate(s) for s in syms ] else: syms = [ separate(syms) ] collected, disliked = {}, Rational(0) for product in summa: terms = [ parse_term(i) for i in make_list(product, Mul) ] for symbol in syms: result = parse_expression(terms, symbol) if result is not None: terms, elems, common_expo, has_deriv = result # when there was derivative in current pattern we # will need to rebuild its expression from scratch if not has_deriv: index = Pow(symbol, common_expo) else: index = make_expression(elems) terms = separate(make_expression(terms)) index = separate(index) if index in collected: collected[index] += terms else: collected[index] = terms break else: # none of the patterns matched disliked += product if disliked != Rational(0): collected[Rational(1)] = disliked if evaluate: return Add(*[ a*b for a, b in collected.iteritems() ]) else: return collected
def risch_norman(f, x, rewrite=False): """Computes indefinite integral using extended Risch-Norman algorithm, also known as parallel Risch. This is a simplified version of full recursive Risch algorithm. It is designed for integrating various classes of functions including transcendental elementary or special functions like Airy, Bessel, Whittaker and Lambert. The main difference between this algorithm and the recursive one is that rather than computing a tower of differential extensions in a recursive way, it handles all cases in one shot. That's why it is called parallel Risch algorithm. This makes it much faster than the original approach. Another benefit is that it doesn't require to rewrite expressions in terms of complex exponentials. Rather it uses tangents and so antiderivatives are being found in a more familliar form. Risch-Norman algorithm can also handle special functions very easily without any additional effort. Just differentiation method must be known for a given function. Note that this algorithm is not a decision procedure. If it computes an antiderivative for a given integral then it's a proof that such function exists. However when it fails then there still may exist an antiderivative and a fallback to recurrsive Risch algorithm would be necessary. The question if this algorithm can be made a full featured decision procedure still remains open. For more information on the implemented algorithm refer to: [1] K. Geddes, L.Stefanus, On the Risch-Norman Integration Method and its Implementation in Maple, Proceedings of ISSAC'89, ACM Press, 212-217. [2] J. H. Davenport, On the Parallel Risch Algorithm (I), Proceedings of EUROCAM'82, LNCS 144, Springer, 144-157. [3] J. H. Davenport, On the Parallel Risch Algorithm (III): Use of Tangents, SIGSAM Bulletin 16 (1982), 3-6. [4] J. H. Davenport, B. M. Trager, On the Parallel Risch Algorithm (II), ACM Transactions on Mathematical Software 11 (1985), 356-362. """ f = Basic.sympify(f) if not f.has(x): return f * x rewritables = { (sin, cos, cot) : tan, (sinh, cosh, coth) : tanh, } if rewrite: for candidates, rule in rewritables.iteritems(): f = f.rewrite(candidates, rule) else: for candidates in rewritables.iterkeys(): if f.has(*candidates): break else: rewrite = True terms = components(f) for g in set(terms): h = g.diff(x) if not isinstance(h, Basic.Zero): terms |= components(h) terms = [ g for g in terms if g.has(x) ] V, in_terms, out_terms = [], [], {} for i, term in enumerate(terms): V += [ Symbol('x%s' % i) ] N = term.count_ops(symbolic=False) in_terms += [ (N, term, V[-1]) ] out_terms[V[-1]] = term in_terms.sort(lambda u, v: int(v[0] - u[0])) def substitute(expr): for _, g, symbol in in_terms: expr = expr.subs(g, symbol) return expr diffs = [ substitute(g.diff(x)) for g in terms ] denoms = [ g.as_numer_denom()[1] for g in diffs ] denom = reduce(lambda p, q: lcm(p, q, V), denoms) numers = [ normal(denom * g, *V) for g in diffs ] def derivation(h): return Basic.Add(*[ d * h.diff(v) for d, v in zip(numers, V) ]) def deflation(p): for y in p.atoms(Basic.Symbol): if not isinstance(derivation(p), Basic.Zero): c, q = p.as_polynomial(y).as_primitive() return deflation(c) * gcd(q, q.diff(y)) else: return p def splitter(p): for y in p.atoms(Basic.Symbol): if not isinstance(derivation(y), Basic.Zero): c, q = p.as_polynomial(y).as_primitive() q = q.as_basic() h = gcd(q, derivation(q), y) s = quo(h, gcd(q, q.diff(y), y), y) c_split = splitter(c) if s.as_polynomial(y).degree() == 0: return (c_split[0], q * c_split[1]) q_split = splitter(normal(q / s, *V)) return (c_split[0]*q_split[0]*s, c_split[1]*q_split[1]) else: return (S.One, p) special = [] for term in terms: if isinstance(term, Basic.Function): if isinstance(term, Basic.tan): special += [ (1 + substitute(term)**2, False) ] elif isinstance(term.func, tanh): special += [ (1 + substitute(term), False), (1 - substitute(term), False) ] #elif isinstance(term.func, Basic.LambertW): # special += [ (substitute(term), True) ] ff = substitute(f) P, Q = ff.as_numer_denom() u_split = splitter(denom) v_split = splitter(Q) s = u_split[0] * Basic.Mul(*[ g for g, a in special if a ]) a, b, c = [ p.as_polynomial(*V).degree() for p in [s, P, Q] ] candidate_denom = s * v_split[0] * deflation(v_split[1]) monoms = monomials(V, 1 + a + max(b, c)) linear = False while True: coeffs, candidate, factors = [], S.Zero, set() for i, monomial in enumerate(monoms): coeffs += [ Symbol('A%s' % i, dummy=True) ] candidate += coeffs[-1] * monomial candidate /= candidate_denom polys = [ v_split[0], v_split[1], u_split[0]] + [ s[0] for s in special ] for irreducibles in [ factorization(p, linear) for p in polys ]: factors |= irreducibles for i, irreducible in enumerate(factors): if not isinstance(irreducible, Basic.Number): coeffs += [ Symbol('B%s' % i, dummy=True) ] candidate += coeffs[-1] * Basic.log(irreducible) h = together(ff - derivation(candidate) / denom) numerator = h.as_numer_denom()[0].expand() if not isinstance(numerator, Basic.Add): numerator = [numerator] collected = {} for term in numerator: coeff, depend = term.as_independent(*V) if depend in collected: collected[depend] += coeff else: collected[depend] = coeff solutions = solve(collected.values(), coeffs) if solutions is None: if linear: break else: linear = True else: break if solutions is not None: antideriv = candidate.subs_dict(solutions) for C in coeffs: if C not in solutions: antideriv = antideriv.subs(C, S.Zero) antideriv = simplify(antideriv.subs_dict(out_terms)).expand() if isinstance(antideriv, Basic.Add): return Basic.Add(*antideriv.as_coeff_factors()[1]) else: return antideriv else: if not rewrite: return risch_norman(f, x, rewrite=True) else: return None
def indexsymbol(a): if isinstance(a, Symbol): return Symbol(a.name, integer=True) else: return Basic.sympify(a)
def apart(f, z, domain=None, index=None): """Computes full partial fraction decomposition of a univariate rational function over the algebraic closure of its field of definition. Although only gcd operations over the initial field are required, the expansion is returned in a formal form with linear denominators. However it is possible to force expansion of the resulting formal summations, and so factorization over a specified domain is performed. To specify the desired behavior of the algorithm use the 'domain' keyword. Setting it to None, which is done be default, will result in no factorization at all. Otherwise it can be assigned with one of Z, Q, R, C domain specifiers and the formal partial fraction expansion will be rewritten using all possible roots over this domain. If the resulting expansion contains formal summations, then for all those a single dummy index variable named 'a' will be generated. To change this default behavior issue new name via 'index' keyword. For more information on the implemented algorithm refer to: [1] M. Bronstein, B. Salvy, Full partial fraction decomposition of rational functions, in: M. Bronstein, ed., Proceedings ISSAC '93, ACM Press, Kiev, Ukraine, 1993, pp. 157-160. """ f = Basic.sympify(f) if isinstance(f, Basic.Add): return Add(*[ apart(g) for g in f ]) else: if f.is_fraction(z): f = normal(f, z) else: return f P, Q = f.as_numer_denom() if not Q.has(z): return f u = Function('u')(z) if index is None: A = Symbol('a', dummy=True) else: A = Symbol(index) partial, r = div(P, Q, z) f, q, U = r / Q, Q, [] for k, d in enumerate(sqf(q, z)): n, d = k + 1, d.as_basic() U += [ u.diff(z, k) ] h = normal(f * d**n, z) / u**n H, subs = [h], [] for j in range(1, n): H += [ H[-1].diff(z) / j ] for j in range(1, n+1): subs += [ (U[j-1], d.diff(z, j) / j) ] for j in range(0, n): P, Q = together(H[j]).as_numer_denom() for i in range(0, j+1): P = P.subs(*subs[j-i]) Q = Q.subs(*subs[0]) G = gcd(P, d, z) D = quo(d, G, z) g, B, _ = ext_gcd(Q, D, z) b = rem(P * B / g, D, z) term = b.subs(z, A) / (z - A)**(n-j) if domain is None: a = D.diff(z) if not a.has(z): partial += term.subs(A, -D.subs(z, 0) / a) else: partial += Basic.Sum(term, (A, Basic.RootOf(D, z))) else: raise NotImplementedError return partial