def test_RootSum(): f = x**5 + 2*x - 1 assert str( RootSum(f, Lambda(z, z), auto=False)) == "RootSum(x**5 + 2*x - 1)" assert str(RootSum(f, Lambda( z, z**2), auto=False)) == "RootSum(x**5 + 2*x - 1, Lambda(z, z**2))"
def test_Roots(): f = Poly(x**17 + 2 * x - 1, x) assert str(RootsOf(f)) == "RootsOf(x**17 + 2*x - 1, x, domain='ZZ')" assert str(RootOf(f, 0)) == "RootOf(x**17 + 2*x - 1, x, domain='ZZ', index=0)" assert str(RootSum( Lambda(z, z**2), f)) == "RootSum(Lambda(_z, _z**2), x**17 + 2*x - 1, x, domain='ZZ')"
def assemble_partfrac_list(partial_list): r"""Reassemble a full partial fraction decomposition from a structured result obtained by the function ``apart_list``. Examples ======== This example is taken from Bronstein's original paper: >>> from sympy.polys.partfrac import apart_list, assemble_partfrac_list >>> from sympy.abc import x >>> f = 36 / (x**5 - 2*x**4 - 2*x**3 + 4*x**2 + x - 2) >>> pfd = apart_list(f) >>> pfd (1, Poly(0, x, domain='ZZ'), [(Poly(_w - 2, _w, domain='ZZ'), Lambda(_a, 4), Lambda(_a, -_a + x), 1), (Poly(_w**2 - 1, _w, domain='ZZ'), Lambda(_a, -3*_a - 6), Lambda(_a, -_a + x), 2), (Poly(_w + 1, _w, domain='ZZ'), Lambda(_a, -4), Lambda(_a, -_a + x), 1)]) >>> assemble_partfrac_list(pfd) -4/(x + 1) - 3/(x + 1)**2 - 9/(x - 1)**2 + 4/(x - 2) If we happen to know some roots we can provide them easily inside the structure: >>> pfd = apart_list(2/(x**2-2)) >>> pfd (1, Poly(0, x, domain='ZZ'), [(Poly(_w**2 - 2, _w, domain='ZZ'), Lambda(_a, _a/2), Lambda(_a, -_a + x), 1)]) >>> pfda = assemble_partfrac_list(pfd) >>> pfda RootSum(_w**2 - 2, Lambda(_a, _a/(-_a + x)))/2 >>> pfda.doit() -sqrt(2)/(2*(x + sqrt(2))) + sqrt(2)/(2*(x - sqrt(2))) >>> from sympy import Dummy, Poly, Lambda, sqrt >>> a = Dummy("a") >>> pfd = (1, Poly(0, x, domain='ZZ'), [([sqrt(2),-sqrt(2)], Lambda(a, a/2), Lambda(a, -a + x), 1)]) >>> assemble_partfrac_list(pfd) -sqrt(2)/(2*(x + sqrt(2))) + sqrt(2)/(2*(x - sqrt(2))) See Also ======== apart, apart_list """ # Common factor common = partial_list[0] # Polynomial part polypart = partial_list[1] pfd = polypart.as_expr() # Rational parts for r, nf, df, ex in partial_list[2]: if isinstance(r, Poly): # Assemble in case the roots are given implicitly by a polynomials an, nu = nf.variables, nf.expr ad, de = df.variables, df.expr # Hack to make dummies equal because Lambda created new Dummies de = de.subs(ad[0], an[0]) func = Lambda(tuple(an), nu/de**ex) pfd += RootSum(r, func, auto=False, quadratic=False) else: # Assemble in case the roots are given explicitly by a list of algebraic numbers for root in r: pfd += nf(root)/df(root)**ex return common*pfd
def apart(f, z=None, **args): """Computes partial fraction decomposition of a rational function. Given a rational function 'f', performing only gcd operations over the algebraic closure of the initial field of definition, compute full partial fraction decomposition with fractions having linear denominators. For all other kinds of expressions the input is returned in an unchanged form. Note however, that `apart` function can thread over sums and relational operators. Note that no factorization of the initial denominator of `f` is needed. The final decomposition is formed in terms of a sum of RootSum instances. By default RootSum tries to compute all its roots to simplify itself. This behavior can be however avoided by setting the keyword flag evaluate=False, which will make this function return a formal decomposition. >>> from sympy import apart >>> from sympy.abc import x, y >>> apart(y/(x+2)/(x+1), x) y/(1 + x) - y/(2 + x) >>> apart(1/(1+x**5), x, evaluate=False) RootSum(Lambda(_a, -_a/(5*(x - _a))), x**5 + 1, x, domain='ZZ') References ========== .. [Bronstein93] M. Bronstein, B. Salvy, Full partial fraction decomposition of rational functions, Proceedings ISSAC '93, ACM Press, Kiev, Ukraine, 1993, pp. 157-160. """ f = cancel(f) if z is None: symbols = f.atoms(Symbol) if not symbols: return f if len(symbols) == 1: z = list(symbols)[0] else: raise ValueError("multivariate partial fractions are not supported") P, Q = f.as_numer_denom() if not Q.has(z): return f partial, r = div(P, Q, z) f, q, U = r / Q, Q, [] u = Function('u')(z) a = Dummy('a') q = Poly(q, z) for d, n in q.sqf_list(all=True, include=True): b = d.as_basic() U += [ u.diff(z, n-1) ] h = cancel(f*b**n) / 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], b.diff(z, j) / j) ] for j in range(0, n): P, Q = cancel(H[j]).as_numer_denom() for i in range(0, j+1): P = P.subs(*subs[j-i]) Q = Q.subs(*subs[0]) P = Poly(P, z) Q = Poly(Q, z) G = P.gcd(d) D = d.exquo(G) B, g = Q.half_gcdex(D) b = (P * B.exquo(g)).rem(D) numer = b.as_basic() denom = (z-a)**(n-j) expr = numer.subs(z, a) / denom partial += RootSum(Lambda(a, expr), D, **args) return partial
def apart_full_decomposition(P, Q): """ Bronstein's full partial fraction decomposition algorithm. Given a univariate rational function ``f``, performing only GCD operations over the algebraic closure of the initial ground domain of definition, compute full partial fraction decomposition with fractions having linear denominators. Note that no factorization of the initial denominator of ``f`` is performed. The final decomposition is formed in terms of a sum of :class:`RootSum` instances. **References** 1. [Bronstein93]_ """ f, x, U = P / Q, P.gen, [] u = Function('u')(x) a = Dummy('a') partial = S(0) for d, n in Q.sqf_list_include(all=True): b = d.as_expr() U += [u.diff(x, n - 1)] h = cancel(f * b**n) / u**n H, subs = [h], [] for j in range(1, n): H += [H[-1].diff(x) / j] for j in range(1, n + 1): subs += [(U[j - 1], b.diff(x, j) / j)] for j in range(0, n): P, Q = cancel(H[j]).as_numer_denom() for i in range(0, j + 1): P = P.subs(*subs[j - i]) Q = Q.subs(*subs[0]) P = Poly(P, x) Q = Poly(Q, x) G = P.gcd(d) D = d.quo(G) B, g = Q.half_gcdex(D) b = (P * B.quo(g)).rem(D) numer = b.as_expr() denom = (x - a)**(n - j) expr = numer.subs(x, a) / denom partial += RootSum(D, Lambda(a, expr)) return partial
def apart(f, z, **flags): """Compute partial fraction decomposition of a rational function. Given a rational function 'f', performing only gcd operations over the algebraic closue of the initial field of definition, compute full partial fraction decomposition with fractions having linear denominators. For all other kinds of expressions the input is returned in an unchanged form. Note however, that 'apart' function can thread over sums and relational operators. Note that no factorization of the initial denominator of 'f' is needed. The final decomposition is formed in terms of a sum of RootSum instances. By default RootSum tries to compute all its roots to simplify itself. This behaviour can be however avoided by seting the keyword flag evaluate=False, which will make this function return a formal decomposition. >>> from sympy import * >>> x,y = symbols('xy') >>> apart(y/(x+2)/(x+1), x) y/(1 + x) - y/(2 + x) >>> apart(1/(1+x**5), x, evaluate=False) RootSum(Lambda(_a, -1/5/(x - _a)*_a), x**5 + 1, x) 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. """ if not f.has(z): return f f = Poly.cancel(f, z) P, Q = f.as_numer_denom() if not Q.has(z): return f partial, r = div(P, Q, z) f, q, U = r / Q, Q, [] u = Function('u')(z) a = Symbol('a', dummy=True) for k, d in enumerate(poly_sqf(q, z)): n, b = k + 1, d.as_basic() U += [ u.diff(z, k) ] h = together(Poly.cancel(f*b**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], b.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]) P, Q = Poly(P, z), Poly(Q, z) G = poly_gcd(P, d) D = poly_quo(d, G) B, g = poly_half_gcdex(Q, D) b = poly_rem(P * poly_quo(B, g), D) numer = b.as_basic() denom = (z-a)**(n-j) expr = numer.subs(z, a) / denom partial += RootSum(Lambda(a, expr), D, **flags) return partial