def test_polys(): x = Symbol("x") f = Poly(x, x) g = lambda x: x for c in (IntegerPoly, IntegerPoly(x, x), Poly, Poly(x, x)): check(c) for c in (RootOf, RootOf(f, 0), RootsOf, RootsOf(x, x), RootSum, RootSum(g, f)): check(c)
def poly_sturm(f, *symbols): """Computes the Sturm sequence of a given polynomial. Given an univariate, square-free polynomial f(x) returns an associated Sturm sequence f_0(x), ..., f_n(x) defined by: f_0(x), f_1(x) = f(x), f'(x) f_n = -rem(f_{n-2}(x), f_{n-1}(x)) For more information on the implemented algorithm refer to: [1] J.H. Davenport, Y. Siret, E. Tournier, Computer Algebra Systems and Algorithms for Algebraic Computation, Academic Press, London, 1988, pp. 124-128 """ if not isinstance(f, Poly): f = Poly(f, *symbols) elif symbols: raise SymbolsError("Redundant symbols were given") if f.is_multivariate: raise MultivariatePolyError(f) else: f = f.as_squarefree() sturm = [f, f.diff()] while not sturm[-1].is_zero: sturm.append(-poly_div(sturm[-2], sturm[-1])[1]) return sturm[:-1]
def __new__(cls, f, x=None): if not isinstance(f, Poly): f = Poly(f, x) elif x is not None: raise SymbolsError("Redundant symbols were given") if f.is_multivariate: raise MultivariatePolyError(f) return Basic.__new__(cls, f)
def mv_int_div(f, g): q = Poly((), *symbols) r = Poly((), *symbols) while not f.is_zero: lc_f, lc_g = f.LC, g.LC dv = lc_f % lc_g cf = lc_f / lc_g monom = monomial_div(f.LM, g.LM) if dv == 0 and monom is not None: q = q.add_term(cf, monom) f -= g.mul_term(cf, monom) else: r = r.add_term(*f.LT) f = f.kill_lead_term() return q, r
def poly_factors(f, *symbols, **flags): """Factor polynomials over rationals. >>> from sympy import * >>> x, y = symbols("x y") >>> poly_factors(x**2 - y**2, x, y) (1, [(Poly(x - y, x, y), 1), (Poly(x + y, x, y), 1)]) """ if not isinstance(f, Poly): f = Poly(f, *symbols) elif symbols: raise SymbolsError("Redundant symbols were given") symbols = list(f.symbols) try: denom, F = f.as_integer() except CoefficientError: other = set([]) for coeff in f.iter_coeffs(): other |= coeff.atoms(Symbol) symbols += sorted(other) F = Poly(f, *symbols) denom, F = F.as_integer() cont, factors = zzX_factor(zzX_from_poly(F)) for i, (h, k) in enumerate(factors): h = zzX_to_poly(h, *symbols) if f.symbols != symbols: h = h.as_poly(*f.symbols) factors[i] = (h, k) return Rational(cont, denom), factors
def poly_root_factors(f, *symbols, **flags): """Returns all factors of an univariate polynomial. >>> from sympy import * >>> x,y = symbols('xy') >>> factors = poly_root_factors(x**2-y, x) >>> set(f.as_basic() for f in factors) set([x + y**(1/2), x - y**(1/2)]) """ if not isinstance(f, Poly): f = Poly(f, *symbols) elif symbols: raise SymbolsError("Redundant symbols were given") if f.is_multivariate: raise MultivariatePolyError(f) else: x = f.symbols[0] if flags.has_key('multiple'): del flags['multiple'] zeros = roots(f, **flags) if not zeros: return [f] else: factors, N = [], 0 for r, n in zeros.iteritems(): h = Poly([(S.One, 1), (-r, 0)], x) factors, N = factors + [h] * n, N + n if N < f.degree: g = reduce(lambda p, q: p * q, factors) factors.append(poly_div(f, g)[0]) return factors
def poly_factors(f, *symbols, **flags): """Factor polynomials over rationals. >>> from sympy import * >>> x, y = symbols("x y") >>> poly_factors(x**2 - y**2, x, y) (1, [(Poly(x - y, x, y), 1), (Poly(x + y, x, y), 1)]) """ if not isinstance(f, Poly): f = Poly(f, *symbols) elif symbols: raise SymbolsError("Redundant symbols were given") denom, f = f.as_integer() if f.is_univariate: coeffs = map(int, f.iter_all_coeffs()) content, factors = zzx_factor(coeffs) for i in xrange(len(factors)): factor, k = factors[i] n = zzx_degree(factor) terms = {} for j, coeff in enumerate(factor): if coeff != 0: terms[(n - j, )] = Integer(coeff) factors[i] = Poly(terms, *f.symbols), k else: content, factors = kronecker_mv(f, **flags) return Rational(content, denom), factors
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 kronecker_mv(f, **flags): """Kronecker method for Z[X] polynomials. NOTE: This function is very slow even on small input. Use debug=True flag to see its progress, if any. """ symbols = f.symbols def mv_int_div(f, g): q = Poly((), *symbols) r = Poly((), *symbols) while not f.is_zero: lc_f, lc_g = f.LC, g.LC dv = lc_f % lc_g cf = lc_f / lc_g monom = monomial_div(f.LM, g.LM) if dv == 0 and monom is not None: q = q.add_term(cf, monom) f -= g.mul_term(cf, monom) else: r = r.add_term(*f.LT) f = f.kill_lead_term() return q, r def combinations(lisp, m): def recursion(fa, lisp, m): if m == 0: yield fa else: for i, fa2 in enumerate(lisp[0:len(lisp) + 1 - m]): for el in recursion(zzx_mul(fa2, fa), list(lisp[i + 1:]), m - 1): yield el for i, fa in enumerate(lisp[0:len(lisp) + 1 - m]): for el in recursion(fa, list(lisp[i + 1:]), m - 1): yield el debug = flags.get('debug', False) cont, f = f.as_primitive() N = len(symbols) max_exp = {} for v in symbols: max_exp[v] = 0 for coeff, monom in f.iter_terms(): for v, exp in zip(symbols, monom): if exp > max_exp[v]: max_exp[v] = exp symbols = sorted(symbols, reverse=True, key=lambda v: max_exp[v]) f = Poly(f, *symbols) d = max_exp[symbols[0]] + 1 terms, exps = {}, [] for i in xrange(0, len(symbols)): exps.append(d**i) for coeff, monom in f.iter_terms(): exp = 0 for i, expi in enumerate(monom): exp += expi * exps[i] terms[exp] = int(coeff) g, factors = zzx_from_dict(terms), [] try: for ff, k in zzx_factor(g)[1]: for i in xrange(0, k): factors.append(ff) except OverflowError: raise PolynomialError( "input too large for multivariate Kronecker method") const, result, tested = 1, [], [] if debug: print "KRONECKER-MV: Z[x] #factors = %i ..." % (len(factors)) for k in range(1, len(factors) // 2 + 1): for h in combinations(factors, k): if h in tested: continue n = zzx_degree(h) terms = {} for coeff in h: if not coeff: n = n - 1 continue else: coeff = Integer(coeff) y_deg, n = n, n - 1 monom = [0] * N for i in xrange(N): v_deg = y_deg % d y_deg = (y_deg - v_deg) // d monom[i] = v_deg monom = tuple(monom) if terms.has_key(monom): terms[monom] += coeff else: terms[monom] = coeff cand = Poly(terms, *symbols) if cand.is_one: continue if cand.LC.is_negative: cand = -cand q, r = mv_int_div(f, cand) if r.is_zero: if debug: print "KRONECKER-MV: Z[X] factor found %s" % cand result.append(cand) f = q else: tested.append(h) if f.is_constant: const, f = f.LC, Poly(1, *symbols) break if f.is_one: break if not f.is_one: if debug: print "KRONECKER-MV: Z[X] factor found %s" % f result.append(f) factors = {} for ff in result: if factors.has_key(ff): factors[ff] += 1 else: factors[ff] = 1 return cont * const, sorted(factors.items())
def number_of_real_roots(f, *symbols, **flags): """Returns the number of distinct real roots of f in (inf, sup]. >>> from sympy import * >>> x,y = symbols('xy') >>> f = Poly(x**2 - 1, x) Count real roots in the (-oo, oo) interval: >>> number_of_real_roots(f) 2 Count real roots in the (0, 2) interval: >>> number_of_real_roots(f, inf=0, sup=2) 1 Count real roots in the (sqrt(2), oo) interval: >>> number_of_real_roots(f, inf=sqrt(2)) 0 For more information on the implemented algorithm refer to: [1] J.H. Davenport, Y. Siret, E. Tournier, Computer Algebra Systems and Algorithms for Algebraic Computation, Academic Press, London, 1988, pp. 124-128 """ def sign_changes(seq): count = 0 for i in xrange(1, len(seq)): if (seq[i-1] < 0 and seq[i] >= 0) or \ (seq[i-1] > 0 and seq[i] <= 0): count += 1 return count if not isinstance(f, Poly): f = Poly(f, *symbols) elif symbols: raise SymbolsError("Redundant symbols were given") if f.is_multivariate: raise MultivariatePolyError(f) if f.degree < 1: return 0 inf = flags.get('inf', None) if inf is not None: inf = sympify(inf) if not inf.is_number: raise ValueError("Not a number: %s" % inf) elif abs(inf) is S.Infinity: inf = None sup = flags.get('sup', None) if sup is not None: sup = sympify(sup) if not sup.is_number: raise ValueError("Not a number: %s" % sup) elif abs(sup) is S.Infinity: sup = None sturm = poly_sturm(f) if inf is None: signs_inf = sign_changes([s.LC * (-1)**s.LM[0] for s in sturm]) else: signs_inf = sign_changes([s(inf) for s in sturm]) if sup is None: signs_sup = sign_changes([s.LC for s in sturm]) else: signs_sup = sign_changes([s(sup) for s in sturm]) return abs(signs_inf - signs_sup)
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 are used in the algorithm. To disable them because of unreadable output set cubics=False or quartics=False 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 _update_dict(result, root, k): if root in result: result[root] += k else: result[root] = k 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_heuristics(f): """Find roots using formulas and some tricks. """ if f.length == 1: if f.is_constant: return [] else: return [S(0)] * f.degree if f.length == 2: if f.degree == 1: return roots_linear(f) else: return roots_binomial(f) x, result = f.symbols[0], [] for i in [S(-1), S(1)]: if f(i).expand().is_zero: f = poly_div(f, x - i)[0] result.append(i) break n = f.degree if n == 1: result += roots_linear(f) elif n == 2: result += roots_quadratic(f) elif n == 3 and flags.get('cubics', True): result += roots_cubic(f) elif n == 4 and flags.get('quartics', False): result += roots_quartic(f) return result multiple = flags.get('multiple', False) if f.length == 1: if f.is_constant: if multiple: return [] else: return {} else: result = {S(0): f.degree} else: (k, ), f = f.as_reduced() if k == 0: zeros = {} else: zeros = {S(0): k} result = {} if f.length == 2: if f.degree == 1: result[roots_linear(f)[0]] = 1 else: for r in roots_binomial(f): _update_dict(result, r, 1) elif f.degree == 2: for r in roots_quadratic(f): _update_dict(result, r, 1) else: try: _, factors = poly_factors(f) if len(factors) == 1 and factors[0][1] == 1: raise CoefficientError for factor, k in factors: for r in _try_heuristics(factor): _update_dict(result, r, k) except CoefficientError: result = _try_decompose(f) 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 test_Roots(): f = Poly(x**17 + 2 * x - 1, x) assert str(RootsOf(f)) == "RootsOf(x**17 + 2*x - 1, x)" assert str(RootOf(f, 0)) == "RootOf(x**17 + 2*x - 1, x, index=0)" assert str(RootSum(Lambda(z, z**2), f)) == "RootSum(Lambda(_z, _z**2), x**17 + 2*x - 1, x)"
def test_Poly(): assert str(Poly(0, x)) == "Poly(0, x)" assert str(Poly(1, x)) == "Poly(1, x)" assert str(Poly(x, x)) == "Poly(x, x)" assert str(Poly(2 * x + 1, x)) == "Poly(2*x + 1, x)" assert str(Poly(2 * x - 1, x)) == "Poly(2*x - 1, x)" assert str(Poly(-1, x)) == "Poly(-1, x)" assert str(Poly(-x, x)) == "Poly(-x, x)" assert str(Poly(-2 * x + 1, x)) == "Poly(-2*x + 1, x)" assert str(Poly(-2 * x - 1, x)) == "Poly(-2*x - 1, x)" assert str(Poly(x - 1, x, order='lex')) == "Poly(x - 1, x)" assert str(Poly(x**2 + 1 + y, x)) == "Poly(x**2 + 1 + y, x)" assert str(Poly(x**2 - 1 + y, x)) == "Poly(x**2 - 1 + y, x)" assert str(Poly(-x * y * z + x * y - 1, x, y, z)) == "Poly(-x*y*z + x*y - 1, x, y, z)" assert str(Poly(-w*x**21*y**7*z + (1 + w)*z**3 - 2*x*z + 1, x, y, z)) == \ "Poly(-w*x**21*y**7*z + (1 + w)*z**3 - 2*x*z + 1, x, y, z)" assert str(Poly(x*y*z**2 - 27*x, x, y, z, order='lex')) == \ "Poly(x*y*z**2 - 27*x, x, y, z, order='lex')" assert str(Poly(x*y*z**2 - 27*x, x, y, z, order='grlex')) == \ "Poly(x*y*z**2 - 27*x, x, y, z)" assert str(Poly(x*y*z**2 - 27*x, x, y, z, order='grevlex')) == \ "Poly(x*y*z**2 - 27*x, x, y, z, order='grevlex')"
def test_Poly(): sT(Poly(7, x), "Poly([(Integer(7), (0,))], Symbol('x'), order='grlex')") sT(Poly(2*x*y + 7, x, y), "Poly([(Integer(2), (1, 1)), (Integer(7), (0, 0))], Symbol('x'), Symbol('y'), order='grlex')") sT(Poly(2*x*y - 7, x, y, order='grevlex'), "Poly([(Integer(2), (1, 1)), (Integer(-7), (0, 0))], Symbol('x'), Symbol('y'), order='grevlex')")