Ejemplo n.º 1
0
    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
Ejemplo n.º 2
0
    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
Ejemplo n.º 3
0
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
Ejemplo n.º 4
0
    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
Ejemplo n.º 5
0
    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
Ejemplo n.º 6
0
    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
Ejemplo n.º 7
0
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
Ejemplo n.º 8
0
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)
Ejemplo n.º 9
0
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
Ejemplo n.º 10
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
Ejemplo n.º 11
0
def indexsymbol(a):
    if isinstance(a, Symbol):
        return Symbol(a.name, integer=True)
    else:
        return Basic.sympify(a)
Ejemplo n.º 12
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