Example #1
0
def test_trim():
    f = Function("f")

    assert trim((f(x) ** 2 + f(x)) / f(x)) == 1 + f(x)
    assert trim((sin(x) ** 2 + sin(x)) / sin(x)) == 1 + sin(x)

    assert trim((f(x) + y * f(x)) / f(x)) == 1 + y

    expr = integrate(1 / (x ** 3 + 1), x)

    assert trim(together(expr.diff(x))) == 1 / (x ** 3 + 1)
    assert cancel(together(expr.diff(x))) == 1 / (x ** 3 + 1)

    expr = together(expr.subs(x, sin(x)).diff(x))

    assert trim(expr) == cos(x) / (1 + sin(x) ** 3)

    assert trim((2 * (1 / n - cos(n * pi) / n)) / pi) == 1 / pi / n * (2 - 2 * cos(pi * n))

    assert trim(sin((f(x) ** 2 + f(x)) / f(x))) == sin(1 + f(x))

    assert trim(exp(x) * sin(x) / 2 + cos(x) * exp(x)) == exp(x) * (sin(x) + 2 * cos(x)) / 2
Example #2
0
def test_trim():
    f = Function('f')

    assert trim((f(x)**2 + f(x)) / f(x)) == 1 + f(x)
    assert trim((sin(x)**2 + sin(x)) / sin(x)) == 1 + sin(x)

    assert trim((f(x) + y * f(x)) / f(x)) == 1 + y

    expr = integrate(1 / (x**3 + 1), x)

    assert trim(together(expr.diff(x))) == 1 / (x**3 + 1)
    assert cancel(together(expr.diff(x))) == 1 / (x**3 + 1)

    expr = together(expr.subs(x, sin(x)).diff(x))

    assert trim(expr) == cos(x) / (1 + sin(x)**3)

    assert trim((2 * (1/n - cos(n * pi)/n))/pi) == \
        1/pi/n*(2 - 2*cos(pi*n))

    assert trim(sin((f(x)**2 + f(x)) / f(x))) == sin(1 + f(x))

    assert trim(exp(x)*sin(x)/2 + cos(x)*exp(x)) == \
        exp(x)*(sin(x) + 2*cos(x))/2
Example #3
0
    def integrate(field=None):
        irreducibles = set()

        for poly in reducibles:
            for z in poly.atoms(Symbol):
                if z in V:
                    break
            else:
                continue

            irreducibles |= set(root_factors(poly, z, domain=field))

        log_coeffs, log_part = [], []
        B = _symbols('B', len(irreducibles))

        for i, poly in enumerate(irreducibles):
            if poly.has(*V):
                log_coeffs.append(B[i])
                log_part.append(log_coeffs[-1] * log(poly))

        coeffs = poly_coeffs + log_coeffs

        candidate = poly_part/poly_denom + Add(*log_part)

        h = together(F - derivation(candidate) / denom)

        numer = h.as_numer_denom()[0].expand()

        if not numer.is_Add:
            numer = [numer]

        equations = {}

        for term in numer.args:
            coeff, dependent = term.as_independent(*V)

            if dependent in equations:
                equations[dependent] += coeff
            else:
                equations[dependent] = coeff

        solution = solve(equations.values(), *coeffs)

        if solution is not None:
            return (solution, candidate, coeffs)
        else:
            return None
Example #4
0
    def integrate(field=None):
        irreducibles = set()

        for poly in reducibles:
            for z in poly.atoms(Symbol):
                if z in V:
                    break
            else:
                continue

            irreducibles |= set(root_factors(poly, z, domain=field))

        log_coeffs, log_part = [], []
        B = _symbols('B', len(irreducibles))

        for i, poly in enumerate(irreducibles):
            if poly.has(*V):
                log_coeffs.append(B[i])
                log_part.append(log_coeffs[-1] * log(poly))

        coeffs = poly_coeffs + log_coeffs

        candidate = poly_part / poly_denom + Add(*log_part)

        h = together(F - derivation(candidate) / denom)

        numer = h.as_numer_denom()[0].expand()

        if not numer.is_Add:
            numer = [numer]

        equations = {}

        for term in numer.args:
            coeff, dependent = term.as_independent(*V)

            if dependent in equations:
                equations[dependent] += coeff
            else:
                equations[dependent] = coeff

        solution = solve(equations.values(), *coeffs)

        if solution is not None:
            return (solution, candidate, coeffs)
        else:
            return None
Example #5
0
 def together(self, *args, **kwargs):
     """See the together function in sympy.simplify"""
     from sympy.simplify import together
     return together(self, *args, **kwargs)
Example #6
0
 def together(self, *args, **kwargs):
     """See the together function in sympy.simplify"""
     from sympy.simplify import together
     return together(self, *args, **kwargs)
Example #7
0
def trim(f, *symbols, **flags):
    """Cancel common factors in a given formal rational expression.

       Given an arbitrary expression, map all functional components
       to temporary symbols, rewriting this expression to rational
       function form and perform cancelation of common factors.

       When given a rational function or a list of symbols discards
       all functional components, then this procedure is equivalent
       to cancel().

       Note that this procedure can thread over composite objects
       like big operators, matrices, relational operators etc. It
       can be also called recursively (to change this behaviour
       unset 'recursive' flag).

       >>> from sympy import *

       >>> x,y = symbols('xy')
       >>> f = Function('f')

       >>> trim((f(x)**2+f(x))/f(x))
       1 + f(x)

       >>> trim((x**2+x)/x)
       1 + x

       Recursively simplify expressions:

       >>> trim(sin((f(x)**2+f(x))/f(x)))
       sin(1 + f(x))

    """
    f = sympify(f)

    if isinstance(f, Relational):
        return Relational(trim(f.lhs, *symbols, **flags),
                          trim(f.rhs, *symbols, **flags), f.rel_op)
    #elif isinstance(f, Matrix):
    #    return f.applyfunc(lambda g: trim(g, *symbols, **flags))
    else:
        recursive = flags.get('recursive', True)

        def is_functional(g):
            return not (g.is_Atom or g.is_number) \
                and (not symbols or g.has(*symbols))

        def components(g):
            result = set()

            if is_functional(g):
                if g.is_Add or g.is_Mul:
                    args = []

                    for h in g.args:
                        h, terms = components(h)

                        result |= terms
                        args.append(h)

                    g = g.__class__(*args)
                elif g.is_Pow:
                    if recursive:
                        base = trim(g.base, *symbols, **flags)
                    else:
                        base = g.base

                    if g.exp.is_Rational:
                        if g.exp.is_Integer:
                            if g.exp is S.NegativeOne:
                                h, terms = components(base)
                                return h**S.NegativeOne, terms
                            else:
                                h = base
                        else:
                            h = base**Rational(1, g.exp.q)

                        g = base**g.exp
                    else:
                        if recursive:
                            h = g = base**trim(g.exp, *symbols, **flags)
                        else:
                            h = g = base**g.exp

                    if is_functional(h):
                        result.add(h)
                else:
                    if not recursive:
                        result.add(g)
                    else:
                        g = g.__class__(*[trim(h, *symbols,
                            **flags) for h in g.args])

                        if is_functional(g):
                            result.add(g)

            return g, result

        if f.is_number or not f.has_any_symbols(*symbols):
            return f

        f = together(f.expand())
        f, terms = components(f)

        if not terms:
            return Poly.cancel(f, *symbols)
        else:
            mapping, reverse = {}, {}

            for g in terms:
                mapping[g] = Temporary()
                reverse[mapping[g]] = g

            p, q = f.as_numer_denom()
            f = p.expand()/q.expand()

            if not symbols:
                symbols = tuple(f.atoms(Symbol))

            symbols = tuple(mapping.values()) + symbols

            H = Poly.cancel(f.subs(mapping), *symbols)

            if not flags.get('extract', True):
                return H.subs(reverse)
            else:
                def extract(f):
                    p = f.args[0]

                    for q in f.args[1:]:
                        p = gcd(p, q, *symbols)

                        if p.is_number:
                            return S.One, f

                    return p, Add(*[quo(g, p, *symbols) for g in f.args])

                P, Q = H.as_numer_denom()

                if P.is_Add:
                    GP, P = extract(P)
                else:
                    GP = S.One

                if Q.is_Add:
                    GQ, Q = extract(Q)
                else:
                    GQ = S.One

                return ((GP*P)/(GQ*Q)).subs(reverse)
Example #8
0
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
Example #9
0
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
Example #10
0
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