def roots_cyclotomic(f, factor=False): """Compute roots of cyclotomic polynomials. """ L, U = _inv_totient_estimate(f.degree()) for n in xrange(L, U + 1): g = cyclotomic_poly(n, f.gen, polys=True) if f == g: break else: # pragma: no cover raise RuntimeError("failed to find index of a cyclotomic polynomial") roots = [] if not factor: for k in xrange(1, n + 1): if igcd(k, n) == 1: roots.append(exp(2*k*S.Pi*I/n).expand(complex=True)) else: g = Poly(f, extension=(-1)**Rational(1, n)) for h, _ in g.factor_list()[1]: roots.append(-h.TC()) return sorted(roots, key=default_sort_key)
def roots_cyclotomic(f, factor=False): """Compute roots of cyclotomic polynomials. """ L, U = _inv_totient_estimate(f.degree()) for n in range(L, U + 1): g = cyclotomic_poly(n, f.gen, polys=True) if f == g: break else: # pragma: no cover raise RuntimeError("failed to find index of a cyclotomic polynomial") roots = [] if not factor: # get the indices in the right order so the computed # roots will be sorted h = n//2 ks = [i for i in range(1, n + 1) if igcd(i, n) == 1] ks.sort(key=lambda x: (x, -1) if x <= h else (abs(x - n), 1)) d = 2*I*pi/n for k in reversed(ks): roots.append(exp(k*d).expand(complex=True)) else: g = Poly(f, extension=root(-1, n)) for h, _ in ordered(g.factor_list()[1]): roots.append(-h.TC()) return roots
def random_poly(x, n, inf, sup, domain=ZZ, polys=False): """Return a polynomial of degree ``n`` with coefficients in ``[inf, sup]``. """ poly = Poly(dup_random(n, inf, sup, domain), x, domain=domain) if not polys: return poly.as_expr() else: return poly
def as_poly(self, x=None): """Create a Poly instance from ``self``. """ if x is not None: return Poly.new(self.rep, x) else: if self.alias is not None: return Poly.new(self.rep, self.alias) else: return PurePoly.new(self.rep, Dummy('x'))
def _minimal_polynomial_sq(p, n, x): """ Returns the minimal polynomial for the ``nth-root`` of a sum of surds or ``None`` if it fails. Parameters ========== p : sum of surds n : positive integer x : variable of the returned polynomial Examples ======== >>> from sympy.polys.numberfields import _minimal_polynomial_sq >>> from sympy import sqrt >>> from sympy.abc import x >>> q = 1 + sqrt(2) + sqrt(3) >>> _minimal_polynomial_sq(q, 3, x) x**12 - 4*x**9 - 4*x**6 + 16*x**3 - 8 """ from sympy.simplify.simplify import _is_sum_surds p = sympify(p) n = sympify(n) r = _is_sum_surds(p) if not n.is_Integer or not n > 0 or not _is_sum_surds(p): return None pn = p**Rational(1, n) # eliminate the square roots p -= x while 1: p1 = _separate_sq(p) if p1 is p: p = p1.subs({x:x**n}) break else: p = p1 # _separate_sq eliminates field extensions in a minimal way, so that # if n = 1 then `p = constant*(minimal_polynomial(p))` # if n > 1 it contains the minimal polynomial as a factor. if n == 1: p1 = Poly(p) if p.coeff(x**p1.degree(x)) < 0: p = -p p = p.primitive()[1] return p # by construction `p` has root `pn` # the minimal polynomial is the factor vanishing in x = pn factors = factor_list(p)[1] result = _choose_factor(factors, x, pn) return result
def _eval_sum_hyper(f, i, a): """ Returns (res, cond). Sums from a to oo. """ from sympy.functions import hyper from sympy.simplify import hyperexpand, hypersimp, fraction, simplify from sympy.polys.polytools import Poly, factor from sympy.core.numbers import Float if a != 0: return _eval_sum_hyper(f.subs(i, i + a), i, 0) if f.subs(i, 0) == 0: if simplify(f.subs(i, Dummy('i', integer=True, positive=True))) == 0: return S(0), True return _eval_sum_hyper(f.subs(i, i + 1), i, 0) hs = hypersimp(f, i) if hs is None: return None if isinstance(hs, Float): from sympy.simplify.simplify import nsimplify hs = nsimplify(hs) numer, denom = fraction(factor(hs)) top, topl = numer.as_coeff_mul(i) bot, botl = denom.as_coeff_mul(i) ab = [top, bot] factors = [topl, botl] params = [[], []] for k in range(2): for fac in factors[k]: mul = 1 if fac.is_Pow: mul = fac.exp fac = fac.base if not mul.is_Integer: return None p = Poly(fac, i) if p.degree() != 1: return None m, n = p.all_coeffs() ab[k] *= m**mul params[k] += [n/m]*mul # Add "1" to numerator parameters, to account for implicit n! in # hypergeometric series. ap = params[0] + [1] bq = params[1] x = ab[0]/ab[1] h = hyper(ap, bq, x) return f.subs(i, 0)*hyperexpand(h), h.convergence_statement
def __new__(cls, expr, coeffs=Tuple(), alias=None, **args): """Construct a new algebraic number. """ expr = sympify(expr) if isinstance(expr, (tuple, Tuple)): minpoly, root = expr if not minpoly.is_Poly: minpoly = Poly(minpoly) elif expr.is_AlgebraicNumber: minpoly, root = expr.minpoly, expr.root else: minpoly, root = minimal_polynomial( expr, args.get('gen'), polys=True), expr dom = minpoly.get_domain() if coeffs != Tuple(): if not isinstance(coeffs, ANP): rep = DMP.from_sympy_list(sympify(coeffs), 0, dom) scoeffs = Tuple(*coeffs) else: rep = DMP.from_list(coeffs.to_list(), 0, dom) scoeffs = Tuple(*coeffs.to_list()) if rep.degree() >= minpoly.degree(): rep = rep.rem(minpoly.rep) sargs = (root, scoeffs) else: rep = DMP.from_list([1, 0], 0, dom) if ask(Q.negative(root)): rep = -rep sargs = (root, coeffs) if alias is not None: if not isinstance(alias, Symbol): alias = Symbol(alias) sargs = sargs + (alias,) obj = Expr.__new__(cls, *sargs) obj.rep = rep obj.root = root obj.alias = alias obj.minpoly = minpoly return obj
def _minpoly_pow(ex, pw, x, dom, mp=None): """ Returns ``minpoly(ex**pw, x)`` Parameters ========== ex : algebraic element pw : rational number x : indeterminate of the polynomial dom: ground domain mp : minimal polynomial of ``p`` Examples ======== >>> from sympy import sqrt, QQ, Rational >>> from sympy.polys.numberfields import _minpoly_pow, minpoly >>> from sympy.abc import x, y >>> p = sqrt(1 + sqrt(2)) >>> _minpoly_pow(p, 2, x, QQ) x**2 - 2*x - 1 >>> minpoly(p**2, x) x**2 - 2*x - 1 >>> _minpoly_pow(y, Rational(1, 3), x, QQ.frac_field(y)) x**3 - y >>> minpoly(y**Rational(1, 3), x) x**3 - y """ pw = sympify(pw) if not mp: mp = _minpoly_compose(ex, x, dom) if not pw.is_rational: raise NotAlgebraic("%s doesn't seem to be an algebraic element" % ex) if pw < 0: if mp == x: raise ZeroDivisionError('%s is zero' % ex) mp = _invertx(mp, x) if pw == -1: return mp pw = -pw ex = 1/ex y = Dummy(str(x)) mp = mp.subs({x: y}) n, d = pw.as_numer_denom() res = Poly(resultant(mp, x**d - y**n, gens=[y]), x, domain=dom) _, factors = res.factor_list() res = _choose_factor(factors, x, ex**pw, dom) return res.as_expr()
def horner(f, *gens, **args): """ Rewrite a polynomial in Horner form. Among other applications, evaluation of a polynomial at a point is optimal when it is applied using the Horner scheme ([1]). Examples ======== >>> from sympy.polys.polyfuncs import horner >>> from sympy.abc import x, y, a, b, c, d, e >>> horner(9*x**4 + 8*x**3 + 7*x**2 + 6*x + 5) x*(x*(x*(9*x + 8) + 7) + 6) + 5 >>> horner(a*x**4 + b*x**3 + c*x**2 + d*x + e) e + x*(d + x*(c + x*(a*x + b))) >>> f = 4*x**2*y**2 + 2*x**2*y + 2*x*y**2 + x*y >>> horner(f, wrt=x) x*(x*y*(4*y + 2) + y*(2*y + 1)) >>> horner(f, wrt=y) y*(x*y*(4*x + 2) + x*(2*x + 1)) References ========== [1] - http://en.wikipedia.org/wiki/Horner_scheme """ allowed_flags(args, []) try: F, opt = poly_from_expr(f, *gens, **args) except PolificationFailed as exc: return exc.expr form, gen = S.Zero, F.gen if F.is_univariate: for coeff in F.all_coeffs(): form = form*gen + coeff else: F, gens = Poly(F, gen), gens[1:] for coeff in F.all_coeffs(): form = form*gen + horner(coeff, *gens, **args) return form
def full_coeffs(p,D,s): ''' Computes coefficients of a polynomial matrix p : polynomial D :degree up to which we want to complete the list with zeroes s: variable of the polynomial matrix Example: TODO ''' p=Poly(p,s,domain='QQ') # in order to use the all_coeffs method of polynomial class c=p.all_coeffs() if len(c)==D+1: return c else: difference=D+1-len(c) return difference*[0] +c
def gegenbauer_poly(n, a, x=None, polys=False): """Generates Gegenbauer polynomial of degree `n` in `x`. Parameters ========== n : int `n` decides the degree of polynomial x : optional a Decides minimal domain for the list of coefficients. polys : bool, optional ``polys=True`` returns an expression, otherwise (default) returns an expression. """ if n < 0: raise ValueError( "can't generate Gegenbauer polynomial of degree %s" % n) K, a = construct_domain(a, field=True) poly = DMP(dup_gegenbauer(int(n), a, K), K) if x is not None: poly = Poly.new(poly, x) else: poly = PurePoly.new(poly, Dummy('x')) return poly if polys else poly.as_expr()
def jacobi_poly(n, a, b, x=None, polys=False): """Generates Jacobi polynomial of degree `n` in `x`. Parameters ========== n : int `n` decides the degree of polynomial a Lower limit of minimal domain for the list of coefficients. b Upper limit of minimal domain for the list of coefficients. x : optional polys : bool, optional ``polys=True`` returns an expression, otherwise (default) returns an expression. """ if n < 0: raise ValueError("can't generate Jacobi polynomial of degree %s" % n) K, v = construct_domain([a, b], field=True) poly = DMP(dup_jacobi(int(n), v[0], v[1], K), K) if x is not None: poly = Poly.new(poly, x) else: poly = PurePoly.new(poly, Dummy('x')) return poly if polys else poly.as_expr()
def laguerre_poly(n, x=None, alpha=None, polys=False): """Generates Laguerre polynomial of degree `n` in `x`. Parameters ========== n : int `n` decides the degree of polynomial x : optional alpha Decides minimal domain for the list of coefficients. polys : bool, optional ``polys=True`` returns an expression, otherwise (default) returns an expression. """ if n < 0: raise ValueError("can't generate Laguerre polynomial of degree %s" % n) if alpha is not None: K, alpha = construct_domain( alpha, field=True) # XXX: ground_field=True else: K, alpha = QQ, QQ(0) poly = DMP(dup_laguerre(int(n), alpha, K), K) if x is not None: poly = Poly.new(poly, x) else: poly = PurePoly.new(poly, Dummy('x')) return poly if polys else poly.as_expr()
def chebyshevu_poly(n, x=None, polys=False): """Generates Chebyshev polynomial of the second kind of degree `n` in `x`. Parameters ========== n : int `n` decides the degree of polynomial x : optional polys : bool, optional ``polys=True`` returns an expression, otherwise (default) returns an expression. """ if n < 0: raise ValueError( "can't generate 2nd kind Chebyshev polynomial of degree %s" % n) poly = DMP(dup_chebyshevu(int(n), ZZ), ZZ) if x is not None: poly = Poly.new(poly, x) else: poly = PurePoly.new(poly, Dummy('x')) return poly if polys else poly.as_expr()
def hermite_poly(n, x=None, **args): """Generates Hermite polynomial of degree `n` in `x`. """ if n < 0: raise ValueError("can't generate Hermite polynomial of degree %s" % n) if x is not None: x = sympify(x) else: x = Dummy('x') poly = Poly(DMP(dup_hermite(int(n), ZZ), ZZ), x) if not args.get('polys', False): return poly.as_basic() else: return poly
def chebyshevu_poly(n, x=None, **args): """Generates Chebyshev polynomial of the second kind of degree `n` in `x`. """ if n < 0: raise ValueError("can't generate 2nd kind Chebyshev polynomial of degree %s" % n) if x is not None: x = sympify(x) else: x = Dummy('x') poly = Poly(DMP(dup_chebyshevu(int(n), ZZ), ZZ), x) if not args.get('polys', False): return poly.as_basic() else: return poly
def laguerre_poly(n, x=None, **args): """Generates Laguerre polynomial of degree `n` in `x`. """ if n < 0: raise ValueError("can't generate Laguerre polynomial of degree %s" % n) if x is not None: x = sympify(x) else: x = Symbol('x', dummy=True) poly = Poly(DMP(dup_laguerre(int(n), QQ), QQ), x) if not args.get('polys', False): return poly.as_basic() else: return poly
def cyclotomic_poly(n, x=None, **args): """Generates cyclotomic polynomial of order `n` in `x`. """ if n <= 0: raise ValueError("can't generate cyclotomic polynomial of order %s" % n) if x is not None: x = sympify(x) else: x = Symbol('x', dummy=True) poly = Poly(DMP(dup_zz_cyclotomic_poly(int(n), ZZ), ZZ), x) if not args.get('polys', False): return poly.as_basic() else: return poly
def chebyshevt_poly(n, x=None, **args): """Generates Chebyshev polynomial of the first kind of degree `n` in `x`. """ if n < 0: raise ValueError("can't generate 1st kind Chebyshev polynomial of degree %s" % n) if x is not None: x = sympify(x) else: x = Symbol('x', dummy=True) poly = Poly(DMP(dup_chebyshevt(int(n), ZZ), ZZ), x) if not args.get('polys', False): return poly.as_basic() else: return poly
def _try_decompose(f): """Find roots using functional decomposition. """ factors, roots = f.decompose(), [] if len(factors) == 1: for root in _try_heuristics(factors[0]): roots.append(root) return (roots, False) add_comment("Use the substitution") t = Dummy("t1") add_eq(t, compose_(factors[1:]).as_expr()) g = Poly(factors[0].as_expr().subs(f.gen, t), t) add_comment("We have") add_eq(g.as_expr(), 0) for root in _try_heuristics(g): roots.append(root) # f(x) = f1(f2(f3(x))) = 0 # f(x) = f1(t) = 0 --> f2(f3(x)) = t1, t2, ..., tn i = 1 for factor in factors[1:]: previous, roots = list(roots), [] h = compose_(factors[i:]).as_expr() add_comment("Therefore") for root in previous: add_eq(h, root) for root in previous: if i < len(factors) - 1: add_comment("Solve the equation") add_eq(h, root) add_comment("Use the substitution") t = Dummy("t" + str(i+1)) add_eq(t, compose_(factors[(i+1):]).as_expr()) g = Poly(factor.as_expr().subs(f.gen, t), t) - Poly(root, t) else: g = factor - Poly(root, f.gen) for root in _try_heuristics(g): roots.append(root) i += 1 return (roots, True)
def root_factors(f, *gens, **args): """ Returns all factors of a univariate polynomial. Examples ======== >>> from sympy.abc import x, y >>> from sympy.polys.polyroots import root_factors >>> root_factors(x**2 - y, x) [x - sqrt(y), x + sqrt(y)] """ args = dict(args) filter = args.pop('filter', None) F = Poly(f, *gens, **args) if not F.is_Poly: return [f] if F.is_multivariate: raise ValueError('multivariate polynomials are not supported') x = F.gens[0] zeros = roots(F, filter=filter) if not zeros: factors = [F] else: factors, N = [], 0 for r, n in ordered(zeros.items()): factors, N = factors + [Poly(x - r, x)]*n, N + n if N < F.degree(): G = reduce(lambda p, q: p*q, factors) factors.append(F.quo(G)) if not isinstance(f, Poly): factors = [ f.as_expr() for f in factors ] return factors
def _imag_count_of_factor(f): """Return the number of imaginary roots for irreducible univariate polynomial ``f``. """ terms = [(i, j) for (i,), j in f.terms()] if any(i % 2 for i, j in terms): return 0 # update signs even = [(i, I**i*j) for i, j in terms] even = Poly.from_dict(dict(even), Dummy('x')) return int(even.count_roots(-oo, oo))
def test_subterm(u, u_diff): substituted = simplify(integrand / u_diff) if symbol not in substituted.free_symbols: # replaced everything already return False substituted_ = substituted.subs(u, u_var).cancel() if symbol not in substituted_.free_symbols: _, denom = integrand.as_numer_denom() _, denom2 = substituted_.as_numer_denom() denom2 = denom2.subs(u_var, u) constant = denom/denom2 if symbol not in constant.free_symbols: return (constant, substituted_ / constant) if u.is_polynomial(symbol): h = Poly(u, symbol) if h.degree() == 1 and h.nth(0) != 0: t = (u_var - h.nth(0)) / h.nth(1) substituted_ = simplify(integrand.subs(symbol, t)) args = list(get_args(substituted_, u_var)) if symbol not in substituted_.free_symbols and len(args) == 1 and args[0] == u_var: return (1/h.nth(1), substituted_) return False
def spherical_bessel_fn(n, x=None, polys=False): """ Coefficients for the spherical Bessel functions. Those are only needed in the jn() function. The coefficients are calculated from: fn(0, z) = 1/z fn(1, z) = 1/z**2 fn(n-1, z) + fn(n+1, z) == (2*n+1)/z * fn(n, z) Parameters ========== n : int `n` decides the degree of polynomial x : optional polys : bool, optional ``polys=True`` returns an expression, otherwise (default) returns an expression. Examples ======== >>> from sympy.polys.orthopolys import spherical_bessel_fn as fn >>> from sympy import Symbol >>> z = Symbol("z") >>> fn(1, z) z**(-2) >>> fn(2, z) -1/z + 3/z**3 >>> fn(3, z) -6/z**2 + 15/z**4 >>> fn(4, z) 1/z - 45/z**3 + 105/z**5 """ if n < 0: dup = dup_spherical_bessel_fn_minus(-int(n), ZZ) else: dup = dup_spherical_bessel_fn(int(n), ZZ) poly = DMP(dup, ZZ) if x is not None: poly = Poly.new(poly, 1/x) else: poly = PurePoly.new(poly, 1/Dummy('x')) return poly if polys else poly.as_expr()
def random_poly(x, n, inf, sup, domain=ZZ, polys=False): """Generates a polynomial of degree ``n`` with coefficients in ``[inf, sup]``. Parameters ---------- x `x` is the independent term of polynomial n : int `n` decides the order of polynomial inf Lower limit of range in which coefficients lie sup Upper limit of range in which coefficients lie domain : optional Decides what ring the coefficients are supposed to belong. Default is set to Integers. polys : bool, optional ``polys=True`` returns an expression, otherwise (default) returns an expression. """ poly = Poly(dup_random(n, inf, sup, domain), x, domain=domain) return poly if polys else poly.as_expr()
def legendre_poly(n, x=None, **args): """Generates Legendre polynomial of degree `n` in `x`. """ if n < 0: raise ValueError("can't generate Legendre polynomial of degree %s" % n) poly = DMP(dup_legendre(int(n), QQ), QQ) if x is not None: poly = Poly.new(poly, x) else: poly = PurePoly.new(poly, Dummy('x')) if not args.get('polys', False): return poly.as_expr() else: return poly
def cyclotomic_poly(n, x=None, **args): """Generates cyclotomic polynomial of order `n` in `x`. """ if n <= 0: raise ValueError("can't generate cyclotomic polynomial of order %s" % n) poly = DMP(dup_zz_cyclotomic_poly(int(n), ZZ), ZZ) if x is not None: poly = Poly.new(poly, x) else: poly = PurePoly.new(poly, Dummy('x')) if not args.get('polys', False): return poly.as_expr() else: return poly
def chebyshevu_poly(n, x=None, **args): """Generates Chebyshev polynomial of the second kind of degree `n` in `x`. """ if n < 0: raise ValueError("can't generate 2nd kind Chebyshev polynomial of degree %s" % n) poly = DMP(dup_chebyshevu(int(n), ZZ), ZZ) if x is not None: poly = Poly.new(poly, x) else: poly = PurePoly.new(poly, Dummy('x')) if not args.get('polys', False): return poly.as_expr() else: return poly
def jacobi_poly(n, a, b, x=None, **args): """Generates Jacobi polynomial of degree `n` in `x`. """ if n < 0: raise ValueError("can't generate Jacobi polynomial of degree %s" % n) K, v = construct_domain([a, b], field=True) poly = DMP(dup_jacobi(int(n), v[0], v[1], K), K) if x is not None: poly = Poly.new(poly, x) else: poly = PurePoly.new(poly, Dummy('x')) if not args.get('polys', False): return poly.as_expr() else: return poly
def spherical_bessel_fn(n, x=None, **args): """ Coefficients for the spherical Bessel functions. Those are only needed in the jn() function. The coefficients are calculated from: fn(0, z) = 1/z fn(1, z) = 1/z**2 fn(n-1, z) + fn(n+1, z) == (2*n+1)/z * fn(n, z) Examples ======== >>> from sympy.polys.orthopolys import spherical_bessel_fn as fn >>> from sympy import Symbol >>> z = Symbol("z") >>> fn(1, z) z**(-2) >>> fn(2, z) -1/z + 3/z**3 >>> fn(3, z) -6/z**2 + 15/z**4 >>> fn(4, z) 1/z - 45/z**3 + 105/z**5 """ from sympy import sympify if n < 0: dup = dup_spherical_bessel_fn_minus(-int(n), ZZ) else: dup = dup_spherical_bessel_fn(int(n), ZZ) poly = DMP(dup, ZZ) if x is not None: poly = Poly.new(poly, 1/x) else: poly = PurePoly.new(poly, 1/Dummy('x')) if not args.get('polys', False): return poly.as_expr() else: return poly
def test_FiniteExtension_from_sympy(): # Test to_sympy/from_sympy K = FiniteExtension(Poly(x**3 + 1, x, domain=ZZ)) xf = K(x) assert K.from_sympy(x) == xf assert K.to_sympy(xf) == x
def _minpoly_op_algebraic_element(op, ex1, ex2, x, dom, mp1=None, mp2=None): """ return the minimal polynomial for ``op(ex1, ex2)`` Parameters ========== op : operation ``Add`` or ``Mul`` ex1, ex2 : expressions for the algebraic elements x : indeterminate of the polynomials dom: ground domain mp1, mp2 : minimal polynomials for ``ex1`` and ``ex2`` or None Examples ======== >>> from sympy import sqrt, Add, Mul, QQ >>> from sympy.polys.numberfields import _minpoly_op_algebraic_element >>> from sympy.abc import x, y >>> p1 = sqrt(sqrt(2) + 1) >>> p2 = sqrt(sqrt(2) - 1) >>> _minpoly_op_algebraic_element(Mul, p1, p2, x, QQ) x - 1 >>> q1 = sqrt(y) >>> q2 = 1 / y >>> _minpoly_op_algebraic_element(Add, q1, q2, x, QQ.frac_field(y)) x**2*y**2 - 2*x*y - y**3 + 1 References ========== [1] http://en.wikipedia.org/wiki/Resultant [2] I.M. Isaacs, Proc. Amer. Math. Soc. 25 (1970), 638 "Degrees of sums in a separable field extension". """ from sympy import gcd y = Dummy(str(x)) if mp1 is None: mp1 = _minpoly_compose(ex1, x, dom) if mp2 is None: mp2 = _minpoly_compose(ex2, y, dom) else: mp2 = mp2.subs({x: y}) if op is Add: # mp1a = mp1.subs({x: x - y}) if dom == QQ: R, X = ring('X', QQ) p1 = R(dict_from_expr(mp1)[0]) p2 = R(dict_from_expr(mp2)[0]) else: (p1, p2), _ = parallel_poly_from_expr((mp1, x - y), x, y) r = p1.compose(p2) mp1a = r.as_expr() elif op is Mul: mp1a = _muly(mp1, x, y) else: raise NotImplementedError('option not available') if op is Mul or dom != QQ: r = resultant(mp1a, mp2, gens=[y, x]) else: r = rs_compose_add(p1, p2) r = expr_from_dict(r.as_expr_dict(), x) deg1 = degree(mp1, x) deg2 = degree(mp2, y) if op is Mul and deg1 == 1 or deg2 == 1: # if deg1 = 1, then mp1 = x - a; mp1a = x - y - a; # r = mp2(x - a), so that `r` is irreducible return r r = Poly(r, x, domain=dom) _, factors = r.factor_list() res = _choose_factor(factors, x, op(ex1, ex2), dom) return res.as_expr()
def test_constantsimp_take_problem(): c = exp(C1) + 2 assert len(Poly(constantsimp(exp(C1) + c + c*x, [C1])).gens) == 2
def test_FiniteExtension_set_domain(): KZ = FiniteExtension(Poly(x**2 + 1, x, domain='ZZ')) KQ = FiniteExtension(Poly(x**2 + 1, x, domain='QQ')) assert KZ.set_domain(QQ) == KQ
def test_sympify_poly(): p = Poly(x**2 + x + 1, x) assert _sympify(p) is p assert sympify(p) is p
def minimal_polynomial(ex, x=None, **args): """Computes the minimal polynomial of an algebraic number. """ generator = numbered_symbols('a', cls=Dummy) mapping, symbols, replace = {}, {}, [] ex = sympify(ex) if x is not None: x = sympify(x) else: x = S.Pure def update_mapping(ex, exp, base=None): a = generator.next() symbols[ex] = a if base is not None: mapping[ex] = a**exp + base else: mapping[ex] = exp.as_expr(a) return a def bottom_up_scan(ex): if ex.is_Atom: if ex is S.ImaginaryUnit: if ex not in mapping: return update_mapping(ex, 2, 1) else: return symbols[ex] elif ex.is_Rational and ex.q != 0: return ex elif ex.is_Add: return Add(*[bottom_up_scan(g) for g in ex.args]) elif ex.is_Mul: return Mul(*[bottom_up_scan(g) for g in ex.args]) elif ex.is_Pow: if ex.exp.is_Rational: if ex.exp < 0 and ex.base.is_Add: coeff, terms = ex.base.as_coeff_add() elt, _ = primitive_element(terms, polys=True) alg = ex.base - coeff # XXX: turn this into eval() inverse = invert(elt.gen + coeff, elt).as_expr() base = inverse.subs(elt.gen, alg).expand() if ex.exp == -1: return bottom_up_scan(base) else: ex = base**(-ex.exp) if not ex.exp.is_Integer: base, exp = (ex.base**ex.exp.p).expand(), Rational( 1, ex.exp.q) else: base, exp = ex.base, ex.exp base = bottom_up_scan(base) expr = base**exp if expr not in mapping: return update_mapping(expr, 1 / exp, -base) else: return symbols[expr] elif ex.is_AlgebraicNumber: if ex.root not in mapping: return update_mapping(ex.root, ex.minpoly) else: return symbols[ex.root] raise NotAlgebraic("%s doesn't seem to be an algebraic number" % ex) polys = args.get('polys', False) if ex.is_AlgebraicNumber: if not polys: return ex.minpoly.as_expr(x) else: return ex.minpoly.replace(x) elif ex.is_Rational and ex.q != 0: result = ex.q * x - ex.p else: F = [x - bottom_up_scan(ex)] + mapping.values() G = groebner(F, symbols.values() + [x], order='lex') _, factors = factor_list(G[-1]) if len(factors) == 1: ((result, _), ) = factors else: for result, _ in factors: if result.subs(x, ex).evalf(chop=True) == 0: break else: # pragma: no cover raise NotImplementedError( "multiple candidates for the minimal polynomial of %s" % ex) if polys: return Poly(result, x, field=True) else: return result
def roots(f, *gens, **flags): """ Computes symbolic roots of a univariate polynomial. Given a univariate polynomial f with symbolic coefficients (or a list of the polynomial's 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 ``filter`` flag with one of the following specifiers: Z, Q, R, I, C. By default all roots are returned (this is equivalent to setting ``filter='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. Examples ======== >>> from sympy import Poly, roots >>> from sympy.abc import x, y >>> roots(x**2 - 1, x) {-1: 1, 1: 1} >>> p = Poly(x**2-1, x) >>> roots(p) {-1: 1, 1: 1} >>> p = Poly(x**2-y, x, y) >>> roots(Poly(p, x)) {-sqrt(y): 1, sqrt(y): 1} >>> roots(x**2 - y, x) {-sqrt(y): 1, sqrt(y): 1} >>> roots([1, 0, -1]) {-1: 1, 1: 1} """ flags = dict(flags) auto = flags.pop('auto', True) cubics = flags.pop('cubics', True) quartics = flags.pop('quartics', True) quintics = flags.pop('quintics', False) multiple = flags.pop('multiple', False) filter = flags.pop('filter', None) predicate = flags.pop('predicate', None) if isinstance(f, list): if gens: raise ValueError('redundant generators given') x = Dummy('x') poly, i = {}, len(f) - 1 for coeff in f: poly[i], i = sympify(coeff), i - 1 f = Poly(poly, x, field=True) else: try: f = Poly(f, *gens, **flags) except GeneratorsNeeded: if multiple: return [] else: return {} if f.is_multivariate: raise PolynomialError('multivariate polynomials are not supported') 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, roots = f.decompose(), [] for root in _try_heuristics(factors[0]): roots.append(root) for factor in factors[1:]: previous, roots = list(roots), [] for root in previous: g = factor - Poly(root, f.gen) for root in _try_heuristics(g): roots.append(root) return roots def _try_heuristics(f): """Find roots using formulas and some tricks. """ if f.is_ground: return [] if f.is_monomial: return [S(0)] * f.degree() if f.length() == 2: if f.degree() == 1: return map(cancel, roots_linear(f)) else: return roots_binomial(f) result = [] for i in [-1, 1]: if not f.eval(i): f = f.quo(Poly(f.gen - i, f.gen)) result.append(i) break n = f.degree() if n == 1: result += map(cancel, roots_linear(f)) elif n == 2: result += map(cancel, roots_quadratic(f)) elif f.is_cyclotomic: result += roots_cyclotomic(f) elif n == 3 and cubics: result += roots_cubic(f) elif n == 4 and quartics: result += roots_quartic(f) elif n == 5 and quintics: result += roots_quintic(f) return result (k, ), f = f.terms_gcd() if not k: zeros = {} else: zeros = {S(0): k} coeff, f = preprocess_roots(f) if auto and f.get_domain().has_Ring: f = f.to_field() result = {} if not f.is_ground: if not f.get_domain().is_Exact: for r in f.nroots(): _update_dict(result, r, 1) elif f.degree() == 1: result[roots_linear(f)[0]] = 1 elif f.degree() == 2: for r in roots_quadratic(f): _update_dict(result, r, 1) elif f.length() == 2: for r in roots_binomial(f): _update_dict(result, r, 1) else: _, factors = Poly(f.as_expr()).factor_list() if len(factors) == 1 and factors[0][1] == 1: for root in _try_decompose(f): _update_dict(result, root, 1) else: for factor, k in factors: for r in _try_heuristics(Poly(factor, f.gen, field=True)): _update_dict(result, r, k) if coeff is not S.One: _result, result, = result, {} for root, k in _result.iteritems(): result[coeff * root] = k result.update(zeros) if filter 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[filter] except KeyError: raise ValueError("Invalid filter: %s" % filter) for zero in dict(result).iterkeys(): if not query(zero): del result[zero] 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 sorted(zeros, key=default_sort_key)
def test_sympy__polys__polytools__Poly(): from sympy.polys.polytools import Poly assert _test_args(Poly(2, x, y))
def _eval_scalar_mul(self, other): mat = [ Poly(a.as_expr() * other, *a.gens) if isinstance(a, Poly) else a * other for a in self._mat ] return self.__class__(self.rows, self.cols, mat, copy=False)
def test__imag_count(): from sympy.polys.rootoftools import _imag_count_of_factor def imag_count(p): return sum( [_imag_count_of_factor(f) * m for f, m in p.factor_list()[1]]) assert imag_count(Poly(x**6 + 10 * x**2 + 1)) == 2 assert imag_count(Poly(x**2)) == 0 assert imag_count(Poly([1] * 3 + [-1], x)) == 0 assert imag_count(Poly(x**3 + 1)) == 0 assert imag_count(Poly(x**2 + 1)) == 2 assert imag_count(Poly(x**2 - 1)) == 0 assert imag_count(Poly(x**4 - 1)) == 2 assert imag_count(Poly(x**4 + 1)) == 0 assert imag_count(Poly([1, 2, 3], x)) == 0 assert imag_count(Poly(x**3 + x + 1)) == 0 assert imag_count(Poly(x**4 + x + 1)) == 0 def q(r1, r2, p): return Poly(((x - r1) * (x - r2)).subs(x, x**p), x) assert imag_count(q(-1, -2, 2)) == 4 assert imag_count(q(-1, 2, 2)) == 2 assert imag_count(q(1, 2, 2)) == 0 assert imag_count(q(1, 2, 4)) == 4 assert imag_count(q(-1, 2, 4)) == 2 assert imag_count(q(-1, -2, 4)) == 0
def test_issue_8316(): f = Poly(7 * x**8 - 9) assert len(f.all_roots()) == 8 f = Poly(7 * x**8 - 10) assert len(f.all_roots()) == 8
def test_issue_7876(): l1 = Poly(x**6 - x + 1, x).all_roots() l2 = [rootof(x**6 - x + 1, i) for i in range(6)] assert frozenset(l1) == frozenset(l2)
def make_extension(K): K = FiniteExtension(Poly(sin(p)**2+cos(p)**2-1, sin(p), domain=K[cos(p)])) K = FiniteExtension(Poly(sin(t)**2+cos(t)**2-1, sin(t), domain=K[cos(t)])) return K
def test_CRootOf_real_roots(): assert Poly(x**5 + x + 1).real_roots() == [rootof(x**3 - x**2 + 1, 0)] assert Poly(x**5 + x + 1).real_roots(radicals=False) == [rootof( x**3 - x**2 + 1, 0)]
def test_FiniteExtension_exquo(): # Test exquo K = FiniteExtension(Poly(x**4 + 1)) xf = K(x) assert K.exquo(xf**2 - 1, xf - 1) == xf + 1
def lfsr_connection_polynomial(s): """ This function computes the lsfr connection polynomial. INPUT: ``s``: a sequence of elements of even length, with entries in a finite field OUTPUT: ``C(x)``: the connection polynomial of a minimal LFSR yielding ``s``. This implements the algorithm in section 3 of J. L. Massey's article [M]_. References ========== .. [M] James L. Massey, "Shift-Register Synthesis and BCH Decoding." IEEE Trans. on Information Theory, vol. 15(1), pp. 122-127, Jan 1969. Examples ======== >>> from sympy.crypto.crypto import lfsr_sequence, lfsr_connection_polynomial >>> from sympy.polys.domains import FF >>> F = FF(2) >>> fill = [F(1), F(1), F(0), F(1)] >>> key = [F(1), F(0), F(0), F(1)] >>> s = lfsr_sequence(key, fill, 20) >>> lfsr_connection_polynomial(s) x**4 + x + 1 >>> fill = [F(1), F(0), F(0), F(1)] >>> key = [F(1), F(1), F(0), F(1)] >>> s = lfsr_sequence(key, fill, 20) >>> lfsr_connection_polynomial(s) x**3 + 1 >>> fill = [F(1), F(0), F(1)] >>> key = [F(1), F(1), F(0)] >>> s = lfsr_sequence(key, fill, 20) >>> lfsr_connection_polynomial(s) x**3 + x**2 + 1 >>> fill = [F(1), F(0), F(1)] >>> key = [F(1), F(0), F(1)] >>> s = lfsr_sequence(key, fill, 20) >>> lfsr_connection_polynomial(s) x**3 + x + 1 """ # Initialization: p = s[0].mod F = FF(p) x = Symbol("x") C = 1 * x**0 B = 1 * x**0 m = 1 b = 1 * x**0 L = 0 N = 0 while N < len(s): if L > 0: dC = Poly(C).degree() r = min(L + 1, dC + 1) coeffsC = [C.subs(x, 0) ] + [C.coeff(x**i) for i in range(1, dC + 1)] d = (s[N].to_int() + sum([coeffsC[i] * s[N - i].to_int() for i in range(1, r)])) % p if L == 0: d = s[N].to_int() * x**0 if d == 0: m += 1 N += 1 if d > 0: if 2 * L > N: C = (C - d * ((b**(p - 2)) % p) * x**m * B).expand() m += 1 N += 1 else: T = C C = (C - d * ((b**(p - 2)) % p) * x**m * B).expand() L = N + 1 - L m = 1 b = d B = T N += 1 dC = Poly(C).degree() coeffsC = [C.subs(x, 0)] + [C.coeff(x**i) for i in range(1, dC + 1)] return sum([ coeffsC[i] % p * x**i for i in range(dC + 1) if coeffsC[i] is not None ])
def solve_riccati(fx, x, b0, b1, b2, gensol=False): """ The main function that gives particular/general solutions to Riccati ODEs that have atleast 1 rational particular solution. """ # Step 1 : Convert to Normal Form a = -b0*b2 + b1**2/4 - b1.diff(x)/2 + 3*b2.diff(x)**2/(4*b2**2) + b1*b2.diff(x)/(2*b2) - \ b2.diff(x, 2)/(2*b2) a_t = a.together() num, den = [Poly(e, x, extension=True) for e in a_t.as_numer_denom()] num, den = num.cancel(den, include=True) # Step 2 presol = [] # Step 3 : a(x) is 0 if num == 0: presol.append(1 / (x + Dummy('C1'))) # Step 4 : a(x) is a non-zero constant elif x not in num.free_symbols.union(den.free_symbols): presol.extend([sqrt(a), -sqrt(a)]) # Step 5 : Find poles and valuation at infinity poles = roots(den, x) poles, muls = list(poles.keys()), list(poles.values()) val_inf = val_at_inf(num, den, x) if len(poles): # Check necessary conditions (outlined in the module docstring) if not check_necessary_conds(val_inf, muls): raise ValueError("Rational Solution doesn't exist") # Step 6 # Construct c-vectors for each singular point c = construct_c(num, den, x, poles, muls) # Construct d vectors for each singular point d = construct_d(num, den, x, val_inf) # Step 7 : Iterate over all possible combinations and return solutions # For each possible combination, generate an array of 0's and 1's # where 0 means pick 1st choice and 1 means pick the second choice. # NOTE: We could exit from the loop if we find 3 particular solutions, # but it is not implemented here as - # a. Finding 3 particular solutions is very rare. Most of the time, # only 2 particular solutions are found. # b. In case we exit after finding 3 particular solutions, it might # happen that 1 or 2 of them are redundant solutions. So, instead of # spending some more time in computing the particular solutions, # we will end up computing the general solution from a single # particular solution which is usually slower than computing the # general solution from 2 or 3 particular solutions. c.append(d) choices = product(*c) for choice in choices: m, ybar = compute_m_ybar(x, poles, choice, -val_inf // 2) numy, deny = [ Poly(e, x, extension=True) for e in ybar.together().as_numer_denom() ] # Step 10 : Check if a valid solution exists. If yes, also check # if m is a non-negative integer if m.is_nonnegative == True and m.is_integer == True: # Step 11 : Find polynomial solutions of degree m for the auxiliary equation psol, coeffs, exists = solve_aux_eq(num, den, numy, deny, x, m) # Step 12 : If valid polynomial solution exists, append solution. if exists: # m == 0 case if psol == 1 and coeffs == 0: # p(x) = 1, so p'(x)/p(x) term need not be added presol.append(ybar) # m is a positive integer and there are valid coefficients elif len(coeffs): # Substitute the valid coefficients to get p(x) psol = psol.xreplace(coeffs) # y(x) = ybar(x) + p'(x)/p(x) presol.append(ybar + psol.diff(x) / psol) # Remove redundant solutions from the list of existing solutions remove = set() for i in range(len(presol)): for j in range(i + 1, len(presol)): rem = remove_redundant_sols(presol[i], presol[j], x) if rem is not None: remove.add(rem) sols = [x for x in presol if x not in remove] # Step 15 : Inverse transform the solutions of the equation in normal form bp = -b2.diff(x) / (2 * b2**2) - b1 / (2 * b2) # If general solution is required, compute it from the particular solutions if gensol: sols = [get_gen_sol_from_part_sol(sols, a, x)] # Inverse transform the particular solutions presol = [ Eq(fx, riccati_inverse_normal(y, x, b1, b2, bp).cancel(extension=True)) for y in sols ] return presol
""" allowed_flags(args, []) try: F, opt = poly_from_expr(f, *gens, **args) except PolificationFailed, exc: return exc.expr form, gen = S.Zero, F.gen if F.is_univariate: for coeff in F.all_coeffs(): form = form*gen + coeff else: F, gens = Poly(F, gen), gens[1:] for coeff in F.all_coeffs(): form = form*gen + horner(coeff, *gens, **args) return form def interpolate(data, x): """ Construct an interpolating polynomial for the data points. **Examples** >>> from sympy.polys.polyfuncs import interpolate >>> from sympy.abc import x
def rational_laurent_series(num, den, x, r, m, n): r""" The function computes the Laurent series coefficients of a rational function. Parameters ========== num: A Poly object that is the numerator of `f(x)`. den: A Poly object that is the denominator of `f(x)`. x: The variable of expansion of the series. r: The point of expansion of the series. m: Multiplicity of r if r is a pole of `f(x)`. Should be zero otherwise. n: Order of the term upto which the series is expanded. Returns ======= series: A dictionary that has power of the term as key and coefficient of that term as value. Below is a basic outline of how the Laurent series of a rational function `f(x)` about `x_0` is being calculated - 1. Substitute `x + x_0` in place of `x`. If `x_0` is a pole of `f(x)`, multiply the expression by `x^m` where `m` is the multiplicity of `x_0`. Denote the the resulting expression as g(x). We do this substitution so that we can now find the Laurent series of g(x) about `x = 0`. 2. We can then assume that the Laurent series of `g(x)` takes the following form - .. math:: g(x) = \frac{num(x)}{den(x)} = \sum_{m = 0}^{\infty} a_m x^m where `a_m` denotes the Laurent series coefficients. 3. Multiply the denominator to the RHS of the equation and form a recurrence relation for the coefficients `a_m`. """ one = Poly(1, x, extension=True) if r == oo: # Series at x = oo is equal to first transforming # the function from x -> 1/x and finding the # series at x = 0 num, den = inverse_transform_poly(num, den, x) r = S(0) if r: # For an expansion about a non-zero point, a # transformation from x -> x + r must be made num = num.transform(Poly(x + r, x, extension=True), one) den = den.transform(Poly(x + r, x, extension=True), one) # Remove the pole from the denominator if the series # expansion is about one of the poles num, den = (num * x**m).cancel(den, include=True) # Equate coefficients for the first terms (base case) maxdegree = 1 + max(num.degree(), den.degree()) syms = symbols(f'a:{maxdegree}', cls=Dummy) diff = num - den * Poly(syms[::-1], x) coeff_diffs = diff.all_coeffs()[::-1][:maxdegree] (coeffs, ) = linsolve(coeff_diffs, syms) # Use the recursion relation for the rest recursion = den.all_coeffs()[::-1] div, rec_rhs = recursion[0], recursion[1:] series = list(coeffs) while len(series) < n: next_coeff = Add(*(c * series[-1 - n] for n, c in enumerate(rec_rhs))) / div series.append(-next_coeff) series = {m - i: val for i, val in enumerate(series)} return series
def primitive_element(extension, x=None, **args): """Construct a common number field for all extensions. """ if not extension: raise ValueError("can't compute primitive element for empty extension") if x is not None: x, cls = sympify(x), Poly else: x, cls = Dummy("x"), PurePoly if not args.get("ex", False): gen, coeffs = extension[0], [1] # XXX when minimal_polynomial is extended to work # with AlgebraicNumbers this test can be removed if isinstance(gen, AlgebraicNumber): g = gen.minpoly.replace(x) else: g = minimal_polynomial(gen, x, polys=True) for ext in extension[1:]: _, factors = factor_list(g, extension=ext) g = _choose_factor(factors, x, gen) s, _, g = g.sqf_norm() gen += s * ext coeffs.append(s) if not args.get("polys", False): return g.as_expr(), coeffs else: return cls(g), coeffs generator = numbered_symbols("y", cls=Dummy) F, Y = [], [] for ext in extension: y = next(generator) if ext.is_Poly: if ext.is_univariate: f = ext.as_expr(y) else: raise ValueError("expected minimal polynomial, got %s" % ext) else: f = minpoly(ext, y) F.append(f) Y.append(y) coeffs_generator = args.get("coeffs", _coeffs_generator) for coeffs in coeffs_generator(len(Y)): f = x - sum([c * y for c, y in zip(coeffs, Y)]) G = groebner(F + [f], Y + [x], order="lex", field=True) H, g = G[:-1], cls(G[-1], x, domain="QQ") for i, (h, y) in enumerate(zip(H, Y)): try: H[i] = Poly(y - h, x, domain="QQ").all_coeffs() # XXX: composite=False except CoercionFailed: # pragma: no cover break # G is not a triangular set else: break else: # pragma: no cover raise RuntimeError("run out of coefficient configurations") _, g = g.clear_denoms() if not args.get("polys", False): return g.as_expr(), coeffs, H else: return g, coeffs, H
def q(r1, r2, p): return Poly(((x - r1) * (x - r2)).subs(x, x**p), x)
def primitive_element(extension, x=None, **args): """Construct a common number field for all extensions. """ if not extension: raise ValueError("can't compute primitive element for empty extension") if x is not None: x, cls = sympify(x), Poly else: x, cls = Dummy('x'), PurePoly if not args.get('ex', False): extension = [ AlgebraicNumber(ext, gen=x) for ext in extension ] g, coeffs = extension[0].minpoly.replace(x), [1] for ext in extension[1:]: s, _, g = sqf_norm(g, x, extension=ext) coeffs = [ s*c for c in coeffs ] + [1] if not args.get('polys', False): return g.as_expr(), coeffs else: return cls(g), coeffs generator = numbered_symbols('y', cls=Dummy) F, Y = [], [] for ext in extension: y = next(generator) if ext.is_Poly: if ext.is_univariate: f = ext.as_expr(y) else: raise ValueError("expected minimal polynomial, got %s" % ext) else: f = minpoly(ext, y) F.append(f) Y.append(y) coeffs_generator = args.get('coeffs', _coeffs_generator) for coeffs in coeffs_generator(len(Y)): f = x - sum([ c*y for c, y in zip(coeffs, Y)]) G = groebner(F + [f], Y + [x], order='lex', field=True) H, g = G[:-1], cls(G[-1], x, domain='QQ') for i, (h, y) in enumerate(zip(H, Y)): try: H[i] = Poly(y - h, x, domain='QQ').all_coeffs() # XXX: composite=False except CoercionFailed: # pragma: no cover break # G is not a triangular set else: break else: # pragma: no cover raise RuntimeError("run out of coefficient configurations") _, g = g.clear_denoms() if not args.get('polys', False): return g.as_expr(), coeffs, H else: return g, coeffs, H
def test_gosper_normal(): eq = 4 * n + 5, 2 * (4 * n + 1) * (2 * n + 3), n assert gosper_normal(*eq) == \ (Poly(Rational(1, 4), n), Poly(n + Rational(3, 2)), Poly(n + Rational(1, 4))) assert gosper_normal(*eq, polys=False) == \ (Rational(1, 4), n + Rational(3, 2), n + Rational(1, 4))
def bivariate_type(f, x, y, **kwargs): """Given an expression, f, 3 tests will be done to see what type of composite bivariate it might be, options for u(x, y) are:: x*y x+y x*y+x x*y+y If it matches one of these types, ``u(x, y)``, ``P(u)`` and dummy variable ``u`` will be returned. Solving ``P(u)`` for ``u`` and equating the solutions to ``u(x, y)`` and then solving for ``x`` or ``y`` is equivalent to solving the original expression for ``x`` or ``y``. If ``x`` and ``y`` represent two functions in the same variable, e.g. ``x = g(t)`` and ``y = h(t)``, then if ``u(x, y) - p`` can be solved for ``t`` then these represent the solutions to ``P(u) = 0`` when ``p`` are the solutions of ``P(u) = 0``. Only positive values of ``u`` are considered. Examples ======== >>> from sympy.solvers.solvers import solve >>> from sympy.solvers.bivariate import bivariate_type >>> from sympy.abc import x, y >>> eq = (x**2 - 3).subs(x, x + y) >>> bivariate_type(eq, x, y) (x + y, _u**2 - 3, _u) >>> uxy, pu, u = _ >>> usol = solve(pu, u); usol [sqrt(3)] >>> [solve(uxy - s) for s in solve(pu, u)] [[{x: -y + sqrt(3)}]] >>> all(eq.subs(s).equals(0) for sol in _ for s in sol) True """ u = Dummy('u', positive=True) if kwargs.pop('first', True): p = Poly(f, x, y) f = p.as_expr() _x = Dummy() _y = Dummy() rv = bivariate_type(Poly(f.subs({ x: _x, y: _y }), _x, _y), _x, _y, first=False) if rv: reps = {_x: x, _y: y} return rv[0].xreplace(reps), rv[1].xreplace(reps), rv[2] return p = f f = p.as_expr() # f(x*y) args = Add.make_args(p.as_expr()) new = [] for a in args: a = _mexpand(a.subs(x, u / y)) free = a.free_symbols if x in free or y in free: break new.append(a) else: return x * y, Add(*new), u def ok(f, v, c): new = _mexpand(f.subs(v, c)) free = new.free_symbols return None if (x in free or y in free) else new # f(a*x + b*y) new = [] d = p.degree(x) if p.degree(y) == d: a = root(p.coeff_monomial(x**d), d) b = root(p.coeff_monomial(y**d), d) new = ok(f, x, (u - b * y) / a) if new is not None: return a * x + b * y, new, u # f(a*x*y + b*y) new = [] d = p.degree(x) if p.degree(y) == d: for itry in range(2): a = root(p.coeff_monomial(x**d * y**d), d) b = root(p.coeff_monomial(y**d), d) new = ok(f, x, (u - b * y) / a / y) if new is not None: return a * x * y + b * y, new, u x, y = y, x
def roots_quartic(f): r""" Returns a list of roots of a quartic polynomial. There are many references for solving quartic expressions available [1-5]. This reviewer has found that many of them require one to select from among 2 or more possible sets of solutions and that some solutions work when one is searching for real roots but don't work when searching for complex roots (though this is not always stated clearly). The following routine has been tested and found to be correct for 0, 2 or 4 complex roots. The quasisymmetric case solution [6] looks for quartics that have the form `x**4 + A*x**3 + B*x**2 + C*x + D = 0` where `(C/A)**2 = D`. Although no general solution that is always applicable for all coefficients is known to this reviewer, certain conditions are tested to determine the simplest 4 expressions that can be returned: 1) `f = c + a*(a**2/8 - b/2) == 0` 2) `g = d - a*(a*(3*a**2/256 - b/16) + c/4) = 0` 3) if `f != 0` and `g != 0` and `p = -d + a*c/4 - b**2/12` then a) `p == 0` b) `p != 0` Examples ======== >>> from sympy import Poly, symbols, I >>> from sympy.polys.polyroots import roots_quartic >>> r = roots_quartic(Poly('x**4-6*x**3+17*x**2-26*x+20')) >>> # 4 complex roots: 1+-I*sqrt(3), 2+-I >>> sorted(str(tmp.evalf(n=2)) for tmp in r) ['1.0 + 1.7*I', '1.0 - 1.7*I', '2.0 + 1.0*I', '2.0 - 1.0*I'] References ========== 1. http://mathforum.org/dr.math/faq/faq.cubic.equations.html 2. http://en.wikipedia.org/wiki/Quartic_function#Summary_of_Ferrari.27s_method 3. http://planetmath.org/encyclopedia/GaloisTheoreticDerivationOfTheQuarticFormula.html 4. http://staff.bath.ac.uk/masjhd/JHD-CA.pdf 5. http://www.albmath.org/files/Math_5713.pdf 6. http://www.statemaster.com/encyclopedia/Quartic-equation 7. eqworld.ipmnet.ru/en/solutions/ae/ae0108.pdf """ _, a, b, c, d = f.monic().all_coeffs() if not d: return [S.Zero] + roots([1, a, b, c], multiple=True) elif (c/a)**2 == d: x, m = f.gen, c/a g = Poly(x**2 + a*x + b - 2*m, x) z1, z2 = roots_quadratic(g) h1 = Poly(x**2 - z1*x + m, x) h2 = Poly(x**2 - z2*x + m, x) r1 = roots_quadratic(h1) r2 = roots_quadratic(h2) return r1 + r2 else: a2 = a**2 e = b - 3*a2/8 f = c + a*(a2/8 - b/2) g = d - a*(a*(3*a2/256 - b/16) + c/4) aon4 = a/4 if f is S.Zero: y1, y2 = [sqrt(tmp) for tmp in roots([1, e, g], multiple=True)] return [tmp - aon4 for tmp in [-y1, -y2, y1, y2]] if g is S.Zero: y = [S.Zero] + roots([1, 0, e, f], multiple=True) return [tmp - aon4 for tmp in y] else: # Descartes-Euler method, see [7] sols = _roots_quartic_euler(e, f, g, aon4) if sols: return sols # Ferrari method, see [1, 2] a2 = a**2 e = b - 3*a2/8 f = c + a*(a2/8 - b/2) g = d - a*(a*(3*a2/256 - b/16) + c/4) p = -e**2/12 - g q = -e**3/108 + e*g/3 - f**2/8 TH = Rational(1, 3) def _ans(y): w = sqrt(e + 2*y) arg1 = 3*e + 2*y arg2 = 2*f/w ans = [] for s in [-1, 1]: root = sqrt(-(arg1 + s*arg2)) for t in [-1, 1]: ans.append((s*w - t*root)/2 - aon4) return ans # p == 0 case y1 = -5*e/6 - q**TH if p.is_zero: return _ans(y1) # if p != 0 then u below is not 0 root = sqrt(q**2/4 + p**3/27) r = -q/2 + root # or -q/2 - root u = r**TH # primary root of solve(x**3 - r, x) y2 = -5*e/6 + u - p/u/3 if p.is_nonzero: return _ans(y2) # sort it out once they know the values of the coefficients return [Piecewise((a1, Eq(p, 0)), (a2, True)) for a1, a2 in zip(_ans(y1), _ans(y2))]
def roots_quintic(f): """ Calulate exact roots of a solvable quintic """ result = [] coeff_5, coeff_4, p, q, r, s = f.all_coeffs() # Eqn must be of the form x^5 + px^3 + qx^2 + rx + s if coeff_4: return result if coeff_5 != 1: l = [p/coeff_5, q/coeff_5, r/coeff_5, s/coeff_5] if not all(coeff.is_Rational for coeff in l): return result f = Poly(f/coeff_5) quintic = PolyQuintic(f) # Eqn standardized. Algo for solving starts here if not f.is_irreducible: return result f20 = quintic.f20 # Check if f20 has linear factors over domain Z if f20.is_irreducible: return result # Now, we know that f is solvable for _factor in f20.factor_list()[1]: if _factor[0].is_linear: theta = _factor[0].root(0) break d = discriminant(f) delta = sqrt(d) # zeta = a fifth root of unity zeta1, zeta2, zeta3, zeta4 = quintic.zeta T = quintic.T(theta, d) tol = S(1e-10) alpha = T[1] + T[2]*delta alpha_bar = T[1] - T[2]*delta beta = T[3] + T[4]*delta beta_bar = T[3] - T[4]*delta disc = alpha**2 - 4*beta disc_bar = alpha_bar**2 - 4*beta_bar l0 = quintic.l0(theta) l1 = _quintic_simplify((-alpha + sqrt(disc)) / S(2)) l4 = _quintic_simplify((-alpha - sqrt(disc)) / S(2)) l2 = _quintic_simplify((-alpha_bar + sqrt(disc_bar)) / S(2)) l3 = _quintic_simplify((-alpha_bar - sqrt(disc_bar)) / S(2)) order = quintic.order(theta, d) test = (order*delta.n()) - ( (l1.n() - l4.n())*(l2.n() - l3.n()) ) # Comparing floats if not comp(test, 0, tol): l2, l3 = l3, l2 # Now we have correct order of l's R1 = l0 + l1*zeta1 + l2*zeta2 + l3*zeta3 + l4*zeta4 R2 = l0 + l3*zeta1 + l1*zeta2 + l4*zeta3 + l2*zeta4 R3 = l0 + l2*zeta1 + l4*zeta2 + l1*zeta3 + l3*zeta4 R4 = l0 + l4*zeta1 + l3*zeta2 + l2*zeta3 + l1*zeta4 Res = [None, [None]*5, [None]*5, [None]*5, [None]*5] Res_n = [None, [None]*5, [None]*5, [None]*5, [None]*5] sol = Symbol('sol') # Simplifying improves performace a lot for exact expressions R1 = _quintic_simplify(R1) R2 = _quintic_simplify(R2) R3 = _quintic_simplify(R3) R4 = _quintic_simplify(R4) # Solve imported here. Causing problems if imported as 'solve' # and hence the changed name from sympy.solvers.solvers import solve as _solve a, b = symbols('a b', cls=Dummy) _sol = _solve( sol**5 - a - I*b, sol) for i in range(5): _sol[i] = factor(_sol[i]) R1 = R1.as_real_imag() R2 = R2.as_real_imag() R3 = R3.as_real_imag() R4 = R4.as_real_imag() for i, root in enumerate(_sol): Res[1][i] = _quintic_simplify(root.subs({ a: R1[0], b: R1[1] })) Res[2][i] = _quintic_simplify(root.subs({ a: R2[0], b: R2[1] })) Res[3][i] = _quintic_simplify(root.subs({ a: R3[0], b: R3[1] })) Res[4][i] = _quintic_simplify(root.subs({ a: R4[0], b: R4[1] })) for i in range(1, 5): for j in range(5): Res_n[i][j] = Res[i][j].n() Res[i][j] = _quintic_simplify(Res[i][j]) r1 = Res[1][0] r1_n = Res_n[1][0] for i in range(5): if comp(im(r1_n*Res_n[4][i]), 0, tol): r4 = Res[4][i] break u, v = quintic.uv(theta, d) sqrt5 = math.sqrt(5) # Now we have various Res values. Each will be a list of five # values. We have to pick one r value from those five for each Res u, v = quintic.uv(theta, d) testplus = (u + v*delta*sqrt(5)).n() testminus = (u - v*delta*sqrt(5)).n() # Evaluated numbers suffixed with _n # We will use evaluated numbers for calculation. Much faster. r4_n = r4.n() r2 = r3 = None for i in range(5): r2temp_n = Res_n[2][i] for j in range(5): # Again storing away the exact number and using # evaluated numbers in computations r3temp_n = Res_n[3][j] if( comp( r1_n*r2temp_n**2 + r4_n*r3temp_n**2 - testplus, 0, tol) and comp( r3temp_n*r1_n**2 + r2temp_n*r4_n**2 - testminus, 0, tol ) ): r2 = Res[2][i] r3 = Res[3][j] break if r2: break # Now, we have r's so we can get roots x1 = (r1 + r2 + r3 + r4)/5 x2 = (r1*zeta4 + r2*zeta3 + r3*zeta2 + r4*zeta1)/5 x3 = (r1*zeta3 + r2*zeta1 + r3*zeta4 + r4*zeta2)/5 x4 = (r1*zeta2 + r2*zeta4 + r3*zeta1 + r4*zeta3)/5 x5 = (r1*zeta1 + r2*zeta2 + r3*zeta3 + r4*zeta4)/5 result = [x1, x2, x3, x4, x5] # Now check if solutions are distinct saw = set() for r in result: r = r.n(2) if r in saw: # Roots were identical. Abort, return [] # and fall back to usual solve return [] saw.add(r) return result
def roots_quartic(f): r""" Returns a list of roots of a quartic polynomial. There are many references for solving quartic expressions available [1-5]. This reviewer has found that many of them require one to select from among 2 or more possible sets of solutions and that some solutions work when one is searching for real roots but don't work when searching for complex roots (though this is not always stated clearly). The following routine has been tested and found to be correct for 0, 2 or 4 complex roots. The quasisymmetric case solution [6] looks for quartics that have the form `x**4 + A*x**3 + B*x**2 + C*x + D = 0` where `(C/A)**2 = D`. Although there is a general solution, simpler results can be obtained for certain values of the coefficients. In all cases, 4 roots are returned: 1) `f = c + a*(a**2/8 - b/2) == 0` 2) `g = d - a*(a*(3*a**2/256 - b/16) + c/4) = 0` 3) if `f != 0` and `g != 0` and `p = -d + a*c/4 - b**2/12` then a) `p == 0` b) `p != 0` **Examples** >>> from sympy import Poly, symbols, I >>> from sympy.polys.polyroots import roots_quartic >>> r = roots_quartic(Poly('x**4-6*x**3+17*x**2-26*x+20')) >>> # 4 complex roots: 1+-I*sqrt(3), 2+-I >>> sorted(str(tmp.evalf(n=2)) for tmp in r) ['1.0 + 1.7*I', '1.0 - 1.7*I', '2.0 + 1.0*I', '2.0 - 1.0*I'] **References** 1. http://mathforum.org/dr.math/faq/faq.cubic.equations.html 2. http://en.wikipedia.org/wiki/Quartic_function#Summary_of_Ferrari.27s_method 3. http://planetmath.org/encyclopedia/GaloisTheoreticDerivationOfTheQuarticFormula.html 4. http://staff.bath.ac.uk/masjhd/JHD-CA.pdf 5. http://www.albmath.org/files/Math_5713.pdf 6. http://www.statemaster.com/encyclopedia/Quartic-equation """ _, a, b, c, d = f.monic().all_coeffs() if not d: return [S.Zero] + roots([1, a, b, c], multiple=True) elif (c / a)**2 == d: x, m = f.gen, c / a g = Poly(x**2 + a * x + b - 2 * m, x) z1, z2 = roots_quadratic(g) h1 = Poly(x**2 - z1 * x + m, x) h2 = Poly(x**2 - z2 * x + m, x) r1 = roots_quadratic(h1) r2 = roots_quadratic(h2) return r1 + r2 else: a2 = a**2 e = b - 3 * a2 / 8 f = c + a * (a2 / 8 - b / 2) g = d - a * (a * (3 * a2 / 256 - b / 16) + c / 4) aon4 = a / 4 ans = [] if f is S.Zero: y1, y2 = [tmp**S.Half for tmp in roots([1, e, g], multiple=True)] return [tmp - aon4 for tmp in [-y1, -y2, y1, y2]] if g is S.Zero: y = [S.Zero] + roots([1, 0, e, f], multiple=True) return [tmp - aon4 for tmp in y] else: p = -e**2 / 12 - g q = -e**3 / 108 + e * g / 3 - f**2 / 8 TH = Rational(1, 3) if p is S.Zero: y = -5 * e / 6 - q**TH else: # with p !=0 then u below is not 0 root = sqrt(q**2 / 4 + p**3 / 27) r = -q / 2 + root # or -q/2 - root u = r**TH # primary root of solve(x**3-r, x) y = -5 * e / 6 + u - p / u / 3 w = sqrt(e + 2 * y) arg1 = 3 * e + 2 * y arg2 = 2 * f / w for s in [-1, 1]: root = sqrt(-(arg1 + s * arg2)) for t in [-1, 1]: ans.append((s * w - t * root) / 2 - aon4) return ans
def roots(f, *gens, **flags): """ Computes symbolic roots of a univariate polynomial. Given a univariate polynomial f with symbolic coefficients (or a list of the polynomial's 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. If cubic roots are real but are expressed in terms of complex numbers (casus irreducibilis [1]) the ``trig`` flag can be set to True to have the solutions returned in terms of cosine and inverse cosine functions. To get roots from a specific domain set the ``filter`` flag with one of the following specifiers: Z, Q, R, I, C. By default all roots are returned (this is equivalent to setting ``filter='C'``). By default a dictionary is returned giving a compact result in case of multiple roots. However to get a list containing all those roots set the ``multiple`` flag to True; the list will have identical roots appearing next to each other in the result. (For a given Poly, the all_roots method will give the roots in sorted numerical order.) Examples ======== >>> from sympy import Poly, roots >>> from sympy.abc import x, y >>> roots(x**2 - 1, x) {-1: 1, 1: 1} >>> p = Poly(x**2-1, x) >>> roots(p) {-1: 1, 1: 1} >>> p = Poly(x**2-y, x, y) >>> roots(Poly(p, x)) {-sqrt(y): 1, sqrt(y): 1} >>> roots(x**2 - y, x) {-sqrt(y): 1, sqrt(y): 1} >>> roots([1, 0, -1]) {-1: 1, 1: 1} References ========== 1. http://en.wikipedia.org/wiki/Cubic_function#Trigonometric_.28and_hyperbolic.29_method """ from sympy.polys.polytools import to_rational_coeffs flags = dict(flags) auto = flags.pop('auto', True) cubics = flags.pop('cubics', True) trig = flags.pop('trig', False) quartics = flags.pop('quartics', True) quintics = flags.pop('quintics', False) multiple = flags.pop('multiple', False) filter = flags.pop('filter', None) predicate = flags.pop('predicate', None) if isinstance(f, list): if gens: raise ValueError('redundant generators given') x = Dummy('x') poly, i = {}, len(f) - 1 for coeff in f: poly[i], i = sympify(coeff), i - 1 f = Poly(poly, x, field=True) else: try: f = Poly(f, *gens, **flags) if f.length == 2 and f.degree() != 1: # check for foo**n factors in the constant n = f.degree() npow_bases = [] expr = f.as_expr() con = expr.as_independent(*gens)[0] for p in Mul.make_args(con): if p.is_Pow and not p.exp % n: npow_bases.append(p.base**(p.exp/n)) else: other.append(p) if npow_bases: b = Mul(*npow_bases) B = Dummy() d = roots(Poly(expr - con + B**n*Mul(*others), *gens, **flags), *gens, **flags) rv = {} for k, v in d.items(): rv[k.subs(B, b)] = v return rv except GeneratorsNeeded: if multiple: return [] else: return {} if f.is_multivariate: raise PolynomialError('multivariate polynomials are not supported') 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, roots = f.decompose(), [] for root in _try_heuristics(factors[0]): roots.append(root) for factor in factors[1:]: previous, roots = list(roots), [] for root in previous: g = factor - Poly(root, f.gen) for root in _try_heuristics(g): roots.append(root) return roots def _try_heuristics(f): """Find roots using formulas and some tricks. """ if f.is_ground: return [] if f.is_monomial: return [S(0)]*f.degree() if f.length() == 2: if f.degree() == 1: return list(map(cancel, roots_linear(f))) else: return roots_binomial(f) result = [] for i in [-1, 1]: if not f.eval(i): f = f.quo(Poly(f.gen - i, f.gen)) result.append(i) break n = f.degree() if n == 1: result += list(map(cancel, roots_linear(f))) elif n == 2: result += list(map(cancel, roots_quadratic(f))) elif f.is_cyclotomic: result += roots_cyclotomic(f) elif n == 3 and cubics: result += roots_cubic(f, trig=trig) elif n == 4 and quartics: result += roots_quartic(f) elif n == 5 and quintics: result += roots_quintic(f) return result (k,), f = f.terms_gcd() if not k: zeros = {} else: zeros = {S(0): k} coeff, f = preprocess_roots(f) if auto and f.get_domain().has_Ring: f = f.to_field() rescale_x = None translate_x = None result = {} if not f.is_ground: if not f.get_domain().is_Exact: for r in f.nroots(): _update_dict(result, r, 1) elif f.degree() == 1: result[roots_linear(f)[0]] = 1 elif f.length() == 2: roots_fun = roots_quadratic if f.degree() == 2 else roots_binomial for r in roots_fun(f): _update_dict(result, r, 1) else: _, factors = Poly(f.as_expr()).factor_list() if len(factors) == 1 and f.degree() == 2: for r in roots_quadratic(f): _update_dict(result, r, 1) else: if len(factors) == 1 and factors[0][1] == 1: if f.get_domain().is_EX: res = to_rational_coeffs(f) if res: if res[0] is None: translate_x, f = res[2:] else: rescale_x, f = res[1], res[-1] result = roots(f) if not result: for root in _try_decompose(f): _update_dict(result, root, 1) else: for root in _try_decompose(f): _update_dict(result, root, 1) else: for factor, k in factors: for r in _try_heuristics(Poly(factor, f.gen, field=True)): _update_dict(result, r, k) if coeff is not S.One: _result, result, = result, {} for root, k in _result.items(): result[coeff*root] = k result.update(zeros) if filter 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[filter] except KeyError: raise ValueError("Invalid filter: %s" % filter) for zero in dict(result).keys(): if not query(zero): del result[zero] if predicate is not None: for zero in dict(result).keys(): if not predicate(zero): del result[zero] if rescale_x: result1 = {} for k, v in result.items(): result1[k*rescale_x] = v result = result1 if translate_x: result1 = {} for k, v in result.items(): result1[k + translate_x] = v result = result1 if not multiple: return result else: zeros = [] for zero in ordered(result): zeros.extend([zero]*result[zero]) return zeros
def test_CRootOf___new__(): assert rootof(x, 0) == 0 assert rootof(x, -1) == 0 assert rootof(x, S.Zero) == 0 assert rootof(x - 1, 0) == 1 assert rootof(x - 1, -1) == 1 assert rootof(x + 1, 0) == -1 assert rootof(x + 1, -1) == -1 assert rootof(x**2 + 2 * x + 3, 0) == -1 - I * sqrt(2) assert rootof(x**2 + 2 * x + 3, 1) == -1 + I * sqrt(2) assert rootof(x**2 + 2 * x + 3, -1) == -1 + I * sqrt(2) assert rootof(x**2 + 2 * x + 3, -2) == -1 - I * sqrt(2) r = rootof(x**2 + 2 * x + 3, 0, radicals=False) assert isinstance(r, RootOf) is True r = rootof(x**2 + 2 * x + 3, 1, radicals=False) assert isinstance(r, RootOf) is True r = rootof(x**2 + 2 * x + 3, -1, radicals=False) assert isinstance(r, RootOf) is True r = rootof(x**2 + 2 * x + 3, -2, radicals=False) assert isinstance(r, RootOf) is True assert rootof((x - 1) * (x + 1), 0, radicals=False) == -1 assert rootof((x - 1) * (x + 1), 1, radicals=False) == 1 assert rootof((x - 1) * (x + 1), -1, radicals=False) == 1 assert rootof((x - 1) * (x + 1), -2, radicals=False) == -1 assert rootof((x - 1) * (x + 1), 0, radicals=True) == -1 assert rootof((x - 1) * (x + 1), 1, radicals=True) == 1 assert rootof((x - 1) * (x + 1), -1, radicals=True) == 1 assert rootof((x - 1) * (x + 1), -2, radicals=True) == -1 assert rootof((x - 1) * (x**3 + x + 3), 0) == rootof(x**3 + x + 3, 0) assert rootof((x - 1) * (x**3 + x + 3), 1) == 1 assert rootof((x - 1) * (x**3 + x + 3), 2) == rootof(x**3 + x + 3, 1) assert rootof((x - 1) * (x**3 + x + 3), 3) == rootof(x**3 + x + 3, 2) assert rootof((x - 1) * (x**3 + x + 3), -1) == rootof(x**3 + x + 3, 2) assert rootof((x - 1) * (x**3 + x + 3), -2) == rootof(x**3 + x + 3, 1) assert rootof((x - 1) * (x**3 + x + 3), -3) == 1 assert rootof((x - 1) * (x**3 + x + 3), -4) == rootof(x**3 + x + 3, 0) assert rootof(x**4 + 3 * x**3, 0) == -3 assert rootof(x**4 + 3 * x**3, 1) == 0 assert rootof(x**4 + 3 * x**3, 2) == 0 assert rootof(x**4 + 3 * x**3, 3) == 0 raises(GeneratorsNeeded, lambda: rootof(0, 0)) raises(GeneratorsNeeded, lambda: rootof(1, 0)) raises(PolynomialError, lambda: rootof(Poly(0, x), 0)) raises(PolynomialError, lambda: rootof(Poly(1, x), 0)) raises(PolynomialError, lambda: rootof(x - y, 0)) # issue 8617 raises(PolynomialError, lambda: rootof(exp(x), 0)) raises(NotImplementedError, lambda: rootof(x**3 - x + sqrt(2), 0)) raises(NotImplementedError, lambda: rootof(x**3 - x + I, 0)) raises(IndexError, lambda: rootof(x**2 - 1, -4)) raises(IndexError, lambda: rootof(x**2 - 1, -3)) raises(IndexError, lambda: rootof(x**2 - 1, 2)) raises(IndexError, lambda: rootof(x**2 - 1, 3)) raises(ValueError, lambda: rootof(x**2 - 1, x)) assert rootof(Poly(x - y, x), 0) == y assert rootof(Poly(x**2 - y, x), 0) == -sqrt(y) assert rootof(Poly(x**2 - y, x), 1) == sqrt(y) assert rootof(Poly(x**3 - y, x), 0) == y**Rational(1, 3) assert rootof(y * x**3 + y * x + 2 * y, x, 0) == -1 raises(NotImplementedError, lambda: rootof(x**3 + x + 2 * y, x, 0)) assert rootof(x**3 + x + 1, 0).is_commutative is True
def reduce_poly(self, f, gen=None): r""" Reduce a univariate :py:class:`~.Poly` *f*, or an :py:class:`~.Expr` expressing the same, modulo this :py:class:`~.PrimeIdeal`. Explanation =========== If our second generator $\alpha$ is zero, then we simply reduce the coefficients of *f* mod the rational prime $p$ lying under this ideal. Otherwise we first reduce *f* mod $\alpha$ (as a polynomial in the same variable as *f*), and then mod $p$. Examples ======== >>> from sympy import QQ, cyclotomic_poly, symbols >>> zeta = symbols('zeta') >>> Phi = cyclotomic_poly(7, zeta) >>> k = QQ.algebraic_field((Phi, zeta)) >>> P = k.primes_above(11) >>> frp = P[0] >>> B = [k.to_sympy(a) for a in k.integral_basis()] >>> print([frp.reduce_poly(b, zeta) for b in B]) [1, zeta, zeta**2, -5*zeta**2 - 4*zeta + 1, -zeta**2 - zeta - 5, 4*zeta**2 - zeta - 1] Parameters ========== f : :py:class:`~.Poly`, :py:class:`~.Expr` The univariate polynomial to be reduced. gen : :py:class:`~.Symbol`, None, optional (default=None) Symbol to use as the variable in the polynomials. If *f* is a :py:class:`~.Poly` or a non-constant :py:class:`~.Expr`, this replaces its variable. If *f* is a constant :py:class:`~.Expr`, then *gen* must be supplied. Returns ======= :py:class:`~.Poly`, :py:class:`~.Expr` Type is same as that of given *f*. If returning a :py:class:`~.Poly`, its domain will be the finite field $\mathbb{F}_p$. Raises ====== GeneratorsNeeded If *f* is a constant :py:class:`~.Expr` and *gen* is ``None``. NotImplementedError If *f* is other than :py:class:`~.Poly` or :py:class:`~.Expr`, or is not univariate. """ if isinstance(f, Expr): try: g = Poly(f) except GeneratorsNeeded as e: if gen is None: raise e from None g = Poly(f, gen) return self.reduce_poly(g).as_expr() if isinstance(f, Poly) and f.is_univariate: a = self.alpha.poly(f.gen) if a != 0: f = f.rem(a) return f.set_modulus(self.p) raise NotImplementedError