def ratint_logpart(f, g, x, t=None): """Lazard-Rioboo-Trager algorithm. Given a field K and polynomials f and g in K[x], such that f and g are coprime, deg(f) < deg(g) and g is square-free, returns a list of tuples (s_i, q_i) of polynomials, for i = 1..n, such that s_i in K[t, x] and q_i in K[t], and: ___ ___ d f d \ ` \ ` -- - = -- ) ) a log(s_i(a, x)) dx g dx /__, /__, i=1..n a | q_i(a) = 0 """ f, g = Poly(f, x), Poly(g, x) t = t or Symbol('t', dummy=True) a, b = g, f - g.diff().mul_term(t) res, R = poly_subresultants(a, b) Q = poly_sqf(Poly(res, t)) R_map, H, i = {}, [], 1 for r in R: R_map[r.degree] = r for q in Q: if q.degree > 0: _, q = q.as_primitive() if g.degree == i: H.append((g, q)) else: h = R_map[i] A = poly_sqf(h.LC, t) for j in xrange(0, len(A)): T = poly_gcd(A[j], q)**(j+1) h = poly_div(h, Poly(T, x))[0] # NOTE: h.LC is always invertible in K[t] inv, coeffs = Poly(h.LC, t).invert(q), [S(1)] for coeff in h.coeffs[1:]: T = poly_div(inv*coeff, q)[1] coeffs.append(T.as_basic()) h = Poly(zip(coeffs, h.monoms), x) H.append((h, q)) i += 1 return H
def test_squarefree(): assert Poly(x-1, x).is_squarefree == True assert Poly((x-1)**2, x).is_squarefree == False assert Poly(3*x**2, x).as_squarefree() == Poly(3*x, x) assert Poly(x**2+2*x+1, x).as_squarefree() == Poly(x+1, x) assert Poly(x**5-x**4-x+1, x).as_squarefree() == Poly(x**4-1, x) assert poly_sqf(1, x) == [Poly(1, x)] assert poly_sqf(x, x) == [Poly(x, x)] assert poly_sqf(3*x**2, x) == [Poly(3, x), Poly(x, x)] assert poly_sqf(x**2+2*x+1, x) == [Poly(1, x), Poly(x+1, x)] assert poly_sqf(x**5-x**4-x+1, x) == \ [Poly(x**3 + x**2 + x + 1, x), Poly(x-1, x)] assert poly_sqf(x**8+6*x**6+12*x**4+8*x**2, x) == \ [Poly(1, x), Poly(x, x), Poly(x**2+2, x)] # Bronstein, Symbolic Integration, pp. 52 A = Poly(x**4 - 3*x**2 + 6, x) D = Poly(x**6 - 5*x**4 + 5*x**2 + 4, x) f, g = D, A - D.diff(x).mul_term(t) res, R = poly_subresultants(f, g) S = poly_sqf(Poly(res, t)) assert S == [Poly(45796, t), Poly(1, t), Poly(4*t**2 + 1, t)]
def _try_decompose(f): """Find roots using functional decomposition. """ factors = poly_decompose(f) result, g = {}, factors[0] for i, h in enumerate(poly_sqf(g)): for r in _try_heuristics(h): _update_dict(result, r, i + 1) for factor in factors[1:]: last, result = result.copy(), {} for last_r, i in last.iteritems(): g = factor.sub_term(last_r, (0,)) for j, h in enumerate(poly_sqf(g)): for r in _try_heuristics(h): _update_dict(result, r, i * (j + 1)) return result
def _try_decompose(f): """Find roots using functional decomposition. """ factors = poly_decompose(f) result, g = {}, factors[0] for i, h in enumerate(poly_sqf(g)): for r in _try_heuristics(h): _update_dict(result, r, i + 1) for factor in factors[1:]: last, result = result.copy(), {} for last_r, i in last.iteritems(): g = factor.sub_term(last_r, (0, )) for j, h in enumerate(poly_sqf(g)): for r in _try_heuristics(h): _update_dict(result, r, i * (j + 1)) return result
def test_squarefree(): assert Poly(x-1, x).is_squarefree == True assert Poly((x-1)**2, x).is_squarefree == False assert Poly(3*x**2, x).as_squarefree() == Poly(3*x, x) assert Poly(x**2+2*x+1, x).as_squarefree() == Poly(x+1, x) assert Poly(x**5-x**4-x+1, x).as_squarefree() == Poly(x**4-1, x) assert poly_sqf(1, x) == [Poly(1, x)] assert poly_sqf(x, x) == [Poly(x, x)] assert poly_sqf(3*x**2, x) == [Poly(3, x), Poly(x, x)] assert poly_sqf(x**2+2*x+1, x) == [Poly(1, x), Poly(x+1, x)] assert poly_sqf(x**5-x**4-x+1, x) == \ [Poly(x**3 + x**2 + x + 1, x), Poly(x-1, x)] assert poly_sqf(x**8+6*x**6+12*x**4+8*x**2, x) == \ [Poly(1, x), Poly(x, x), Poly(x**2+2, x)]
def test_squarefree(): assert Poly(x - 1, x).is_squarefree == True assert Poly((x - 1)**2, x).is_squarefree == False assert Poly(3 * x**2, x).as_squarefree() == Poly(3 * x, x) assert Poly(x**2 + 2 * x + 1, x).as_squarefree() == Poly(x + 1, x) assert Poly(x**5 - x**4 - x + 1, x).as_squarefree() == Poly(x**4 - 1, x) assert poly_sqf(1, x) == [Poly(1, x)] assert poly_sqf(x, x) == [Poly(x, x)] assert poly_sqf(3 * x**2, x) == [Poly(3, x), Poly(x, x)] assert poly_sqf(x**2 + 2 * x + 1, x) == [Poly(1, x), Poly(x + 1, x)] assert poly_sqf(x**5-x**4-x+1, x) == \ [Poly(x**3 + x**2 + x + 1, x), Poly(x-1, x)] assert poly_sqf(x**8+6*x**6+12*x**4+8*x**2, x) == \ [Poly(1, x), Poly(x, x), Poly(x**2+2, x)]
def roots(f, *symbols, **flags): """Computes symbolic roots of an univariate polynomial. Given an univariate polynomial f with symbolic coefficients, returns a dictionary with its roots and their multiplicities. Only roots expressible via radicals will be returned. To get a complete set of roots use RootOf class or numerical methods instead. By default cubic and quartic formulas aren't used in the algorithm because of unreadable output. To enable them set cubics=True or quartics=True respectively. To get roots from a specific domain set the 'domain' flag with one of the following specifiers: Z, Q, R, I, C. By default all roots are returned (this is equivalent to setting domain='C'). By default a dictionary is returned giving a compact result in case of multiple roots. However to get a tuple containing all those roots set the 'multiple' flag to True. >>> from sympy import * >>> x,y = symbols('xy') >>> roots(x**2 - 1, x) {1: 1, -1: 1} >>> roots(x**2 - y, x) {y**(1/2): 1, -y**(1/2): 1} """ if not isinstance(f, Poly): f = Poly(f, *symbols) elif symbols: raise SymbolsError("Redundant symbols were given") if f.is_multivariate: raise MultivariatePolyError(f) def roots_trivial(g): if g.length == 1: if g.is_constant: return [] else: return [S.Zero] * g.degree (k,), g = g.as_reduced() if k == 0: zeros = [] else: zeros = [S.Zero] * k if g.length == 2: zeros += roots_binomial(g) else: x = g.symbols[0] for i in [S.NegativeOne, S.One]: if g(i).expand() is S.Zero: g = poly_div(g, x - i)[0] zeros.append(i) break n = g.degree if n == 1: zeros += roots_linear(g) elif n == 2: zeros += roots_quadratic(g) elif n == 3: if flags.get("cubics", False): # TODO: now we can used factor() not only # for cubic polynomials. See #1158 try: _, factors = poly_factors(g) if len(factors) == 1: raise PolynomialError for factor, k in factors: zeros += roots(factor, multiple=True) * k except PolynomialError: zeros += roots_cubic(g) elif n == 4: if flags.get("quartics", False): zeros += roots_quartic(g) return zeros multiple = flags.get("multiple", False) if f.length == 1: if f.is_constant: if multiple: return [] else: return {} else: result = {S.Zero: f.degree} else: (k,), f = f.as_reduced() if k == 0: result = {} else: result = {S.Zero: k} if f.degree == 1: result[roots_linear(f)[0]] = 1 else: factors = poly_decompose(f) zeros, g = {}, factors[0] for i, h in enumerate(poly_sqf(g)): for zero in roots_trivial(h): if zeros.has_key(zero): zeros[zero] += i + 1 else: zeros[zero] = i + 1 for factor in factors[1:]: previous, zeros = zeros.copy(), {} for zero, i in previous.iteritems(): g = factor.sub_term(zero, (0,)) for j, h in enumerate(poly_sqf(g)): for zero in roots_trivial(h): if zeros.has_key(zero): zeros[zero] += i * (j + 1) else: zeros[zero] = i * (j + 1) result.update(zeros) domain = flags.get("domain", None) if domain not in [None, "C"]: handlers = { "Z": lambda r: r.is_Integer, "Q": lambda r: r.is_Rational, "R": lambda r: r.is_real, "I": lambda r: r.is_imaginary, } try: query = handlers[domain] except KeyError: raise ValueError("Invalid domain: %s" % domain) for zero in dict(result).iterkeys(): if not query(zero): del result[zero] predicate = flags.get("predicate", None) if predicate is not None: for zero in dict(result).iterkeys(): if not predicate(zero): del result[zero] if not multiple: return result else: zeros = [] for zero, k in result.iteritems(): zeros.extend([zero] * k) return zeros
def roots(f, *symbols, **flags): """Computes symbolic roots of an univariate polynomial. Given an univariate polynomial f with symbolic coefficients, returns a dictionary with its roots and their multiplicities. Only roots expressible via radicals will be returned. To get a complete set of roots use RootOf class or numerical methods instead. By default cubic and quartic formulas aren't used in the algorithm because of unreadable output. To enable them set cubics=True or quartics=True respectively. To get roots from a specific domain set the 'domain' flag with one of the following specifiers: Z, Q, R, I, C. By default all roots are returned (this is equivalent to setting domain='C'). By default a dictionary is returned giving a compact result in case of multiple roots. However to get a tuple containing all those roots set the 'multiple' flag to True. >>> from sympy import * >>> x,y = symbols('xy') >>> roots(x**2 - 1, x) {1: 1, -1: 1} >>> roots(x**2 - y, x) {y**(1/2): 1, -y**(1/2): 1} """ if not isinstance(f, Poly): f = Poly(f, *symbols) elif symbols: raise SymbolsError("Redundant symbols were given") if f.is_multivariate: raise MultivariatePolyError(f) def roots_trivial(g): if g.length == 1: if g.is_constant: return [] else: return [S.Zero] * g.degree (k, ), g = g.as_reduced() if k == 0: zeros = [] else: zeros = [S.Zero] * k if g.length == 2: zeros += roots_binomial(g) else: x = g.symbols[0] for i in [S.NegativeOne, S.One]: if g(i).expand() is S.Zero: g = poly_div(g, x - i)[0] zeros.append(i) break n = g.degree if n == 1: zeros += roots_linear(g) elif n == 2: zeros += roots_quadratic(g) elif n == 3: if flags.get('cubics', False): # TODO: now we can used factor() not only # for cubic polynomials. See #1158 try: _, factors = poly_factors(g) if len(factors) == 1: raise PolynomialError for factor, k in factors: zeros += roots(factor, multiple=True) * k except PolynomialError: zeros += roots_cubic(g) elif n == 4: if flags.get('quartics', False): zeros += roots_quartic(g) return zeros multiple = flags.get('multiple', False) if f.length == 1: if f.is_constant: if multiple: return [] else: return {} else: result = {S.Zero: f.degree} else: (k, ), f = f.as_reduced() if k == 0: result = {} else: result = {S.Zero: k} if f.degree == 1: result[roots_linear(f)[0]] = 1 else: factors = poly_decompose(f) zeros, g = {}, factors[0] for i, h in enumerate(poly_sqf(g)): for zero in roots_trivial(h): if zeros.has_key(zero): zeros[zero] += i + 1 else: zeros[zero] = i + 1 for factor in factors[1:]: previous, zeros = zeros.copy(), {} for zero, i in previous.iteritems(): g = factor.sub_term(zero, (0, )) for j, h in enumerate(poly_sqf(g)): for zero in roots_trivial(h): if zeros.has_key(zero): zeros[zero] += i * (j + 1) else: zeros[zero] = i * (j + 1) result.update(zeros) domain = flags.get('domain', None) if domain not in [None, 'C']: handlers = { 'Z': lambda r: r.is_Integer, 'Q': lambda r: r.is_Rational, 'R': lambda r: r.is_real, 'I': lambda r: r.is_imaginary, } try: query = handlers[domain] except KeyError: raise ValueError("Invalid domain: %s" % domain) for zero in dict(result).iterkeys(): if not query(zero): del result[zero] predicate = flags.get('predicate', None) if predicate is not None: for zero in dict(result).iterkeys(): if not predicate(zero): del result[zero] if not multiple: return result else: zeros = [] for zero, k in result.iteritems(): zeros.extend([zero] * k) return zeros
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
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