コード例 #1
0
ファイル: risch.py プロジェクト: certik/sympy-oldcore
 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
コード例 #2
0
ファイル: risch.py プロジェクト: certik/sympy-oldcore
    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)
コード例 #3
0
ファイル: simplify.py プロジェクト: certik/sympy-oldcore
def normal(expr, *syms):
    p, q = together(expr).as_numer_denom()

    if p.is_polynomial(*syms) and q.is_polynomial(*syms):
        from sympy.polynomials import gcd, quo

        G = gcd(p, q, syms)

        if not isinstance(G, Basic.One):
            p = quo(p, G, syms)
            q = quo(q, G, syms)

    return p / q
コード例 #4
0
ファイル: gosper.py プロジェクト: certik/sympy-oldcore
def normal(f, g, n):
    """Given relatively prime univariate polynomials 'f' and 'g',
       rewrite their quotient to a normal form defined as follows:

                       f(n)       A(n) C(n+1)
                       ----  =  Z -----------
                       g(n)       B(n)  C(n)

       where Z is arbitrary constant and A, B, C are monic
       polynomials in 'n' with follwing properties:

           (1) gcd(A(n), B(n+h)) = 1 for all 'h' in N
           (2) gcd(B(n), C(n+1)) = 1
           (3) gcd(A(n), C(n)) = 1

       This normal form, or rational factorization in other words,
       is crucial step in Gosper's algorithm and in difference
       equations solving. It can be also used to decide if two
       hypergeometric are similar or not.

       This procedure will return return triple containig elements
       of this factorization in the form (Z*A, B, C). For example:

       >>> from sympy import Symbol
       >>> n = Symbol('n', integer=True)

       >>> normal(4*n+5, 2*(4*n+1)*(2*n+3), n)
       (1/4, 3/2 + n, 1/4 + n)

    """
    f, g = map(Basic.sympify, (f, g))

    if f.is_polynomial:
        p = f.as_polynomial(n)
    else:
        raise ValueError("'f' must be a polynomial")

    if g.is_polynomial:
        q = g.as_polynomial(n)
    else:
        raise ValueError("'g' must be a polynomial")

    a, p = p.as_monic()
    b, q = q.as_monic()

    A = p.sympy_expr
    B = q.sympy_expr

    C, Z = S.One, a / b

    h = Symbol('h', dummy=True)

    res = resultant(A, B.subs(n, n+h), n)

    if not res.is_polynomial(h):
        res = quo(*res.as_numer_denom())

    _nni_roots = nni_roots(res, h)

    if _nni_roots == []:
        return (f, g, S.One)
    else:
        _nni_roots.sort()

        for i in _nni_roots:
            d = gcd(A, B.subs(n, n+i), n)

            A = quo(A, d, n)
            B = quo(B, d.subs(n, n-i), n)

            C *= Mul(*[ d.subs(n, n-j) for j in xrange(1, i+1) ])

        return (Z*A, B, C)
コード例 #5
0
ファイル: simplify.py プロジェクト: certik/sympy-oldcore
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
コード例 #6
0
ファイル: recurr.py プロジェクト: certik/sympy-oldcore
def rsolve_ratio(coeffs, f, n, **hints):
    """Given linear recurrence operator L of order 'k' with polynomial
       coefficients and inhomogeneous equation Ly = f, where 'f' is a
       polynomial, we seek for all rational solutions over field K of
       characteristic zero.

       This procedure accepts only polynomials, however if you are
       interested in solving recurrence with ratinal coefficients
       then use rsolve() with will preprocess equation given and
       run this procedure with polynomial arguments.

       The algorithm performs two basic steps:

           (1) Compute polynomial v(n) which can be used as universal
               denominator of any rational solution of equation Ly = f.

           (2) Construct new linear difference equation by substitution
               y(n) = u(n)/v(n) and solve it for u(n) finding all its
               polynomial solutions. Return None if none were found.

       Algorithm implemented here is a revised version of the original
       Abramov's algorithm, developed in 1989. The new approach is much
       simpler to implement and has better overall efficiency. This
       method can be easily adapted to q-difference equations case.

       Besides finding rational solutions alone, this functions is
       an important part of Hyper algorithm were it is used to find
       particular solution of ingomogeneous part of a recurrence.

       For more information on the implemented algorithm refer to:

       [1] S. A. Abramov, Rational solutions of linear difference
           and q-difference equations with polynomial coefficients,
           in: T. Levelt, ed., Proc. ISSAC '95, ACM Press, New York,
           1995, 285-289

    """
    f = Basic.sympify(f)

    if not f.is_polynomial(n):
        return None

    coeffs = map(Basic.sympify, coeffs)

    r = len(coeffs)-1

    A, B = coeffs[r], coeffs[0]
    A = A.subs(n, n-r).expand()

    h = Symbol('h', dummy=True)

    res = resultant(A, B.subs(n, n+h), n)

    if not res.is_polynomial(h):
        p, q = res.as_numer_denom()
        res = quo(p, q, h)

    _nni_roots = nni_roots(res, h)

    if _nni_roots == []:
        return rsolve_poly(coeffs, f, n, **hints)
    else:
        C, numers = S.One, [S.Zero]*(r+1)

        for i in xrange(int(max(_nni_roots)), -1, -1):
            d = gcd(A, B.subs(n, n+i), n)

            A = quo(A, d, n)
            B = quo(B, d.subs(n, n-i), n)

            C *= Mul(*[ d.subs(n, n-j) for j in xrange(0, i+1) ])

        denoms = [ C.subs(n, n+i) for i in range(0, r+1) ]

        for i in range(0, r+1):
            g = gcd(coeffs[i], denoms[i], n)

            numers[i] = quo(coeffs[i], g, n)
            denoms[i] = quo(denoms[i], g, n)

        for i in xrange(0, r+1):
            numers[i] *= Mul(*(denoms[:i] + denoms[i+1:]))

        result = rsolve_poly(numers, f * Mul(*denoms), n, **hints)

        if result is not None:
            if hints.get('symbols', False):
                return (simplify(result[0] / C), result[1])
            else:
                return simplify(result / C)
        else:
            return None
コード例 #7
0
ファイル: rewrite.py プロジェクト: certik/sympy-oldcore
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