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)
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