Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
"""Sparse polynomial rings. """
from __future__ import print_function, division
from operator import add, mul, lt, le, gt, ge from types import GeneratorType
from sympy.core.expr import Expr from sympy.core.symbol import Symbol, symbols as _symbols from sympy.core.numbers import igcd, oo from sympy.core.sympify import CantSympify, sympify from sympy.core.compatibility import is_sequence, reduce, string_types, range from sympy.ntheory.multinomial import multinomial_coefficients from sympy.polys.monomials import MonomialOps from sympy.polys.orderings import lex from sympy.polys.heuristicgcd import heugcd from sympy.polys.compatibility import IPolys from sympy.polys.polyutils import expr_from_dict, _dict_reorder, _parallel_dict_from_expr from sympy.polys.polyerrors import CoercionFailed, GeneratorsError, GeneratorsNeeded, ExactQuotientFailed, MultivariatePolynomialError from sympy.polys.domains.domainelement import DomainElement from sympy.polys.domains.polynomialring import PolynomialRing from sympy.polys.polyoptions import Domain as DomainOpt, Order as OrderOpt, build_options from sympy.polys.densebasic import dmp_to_dict, dmp_from_dict from sympy.polys.constructor import construct_domain from sympy.printing.defaults import DefaultPrinting from sympy.utilities import public from sympy.utilities.magic import pollute
@public def ring(symbols, domain, order=lex): """Construct a polynomial ring returning ``(ring, x_1, ..., x_n)``.
Parameters ---------- symbols : str, Symbol/Expr or sequence of str, Symbol/Expr (non-empty) domain : :class:`Domain` or coercible order : :class:`Order` or coercible, optional, defaults to ``lex``
Examples ========
>>> from sympy.polys.rings import ring >>> from sympy.polys.domains import ZZ >>> from sympy.polys.orderings import lex
>>> R, x, y, z = ring("x,y,z", ZZ, lex) >>> R Polynomial ring in x, y, z over ZZ with lex order >>> x + y + z x + y + z >>> type(_) <class 'sympy.polys.rings.PolyElement'>
"""
@public def xring(symbols, domain, order=lex): """Construct a polynomial ring returning ``(ring, (x_1, ..., x_n))``.
Parameters ---------- symbols : str, Symbol/Expr or sequence of str, Symbol/Expr (non-empty) domain : :class:`Domain` or coercible order : :class:`Order` or coercible, optional, defaults to ``lex``
Examples ========
>>> from sympy.polys.rings import xring >>> from sympy.polys.domains import ZZ >>> from sympy.polys.orderings import lex
>>> R, (x, y, z) = xring("x,y,z", ZZ, lex) >>> R Polynomial ring in x, y, z over ZZ with lex order >>> x + y + z x + y + z >>> type(_) <class 'sympy.polys.rings.PolyElement'>
""" _ring = PolyRing(symbols, domain, order) return (_ring, _ring.gens)
@public def vring(symbols, domain, order=lex): """Construct a polynomial ring and inject ``x_1, ..., x_n`` into the global namespace.
Parameters ---------- symbols : str, Symbol/Expr or sequence of str, Symbol/Expr (non-empty) domain : :class:`Domain` or coercible order : :class:`Order` or coercible, optional, defaults to ``lex``
Examples ========
>>> from sympy.polys.rings import vring >>> from sympy.polys.domains import ZZ >>> from sympy.polys.orderings import lex
>>> vring("x,y,z", ZZ, lex) Polynomial ring in x, y, z over ZZ with lex order >>> x + y + z x + y + z >>> type(_) <class 'sympy.polys.rings.PolyElement'>
""" _ring = PolyRing(symbols, domain, order) pollute([ sym.name for sym in _ring.symbols ], _ring.gens) return _ring
@public def sring(exprs, *symbols, **options): """Construct a ring deriving generators and domain from options and input expressions.
Parameters ---------- exprs : :class:`Expr` or sequence of :class:`Expr` (sympifiable) symbols : sequence of :class:`Symbol`/:class:`Expr` options : keyword arguments understood by :class:`Options`
Examples ========
>>> from sympy.core import symbols >>> from sympy.polys.rings import sring >>> from sympy.polys.domains import ZZ >>> from sympy.polys.orderings import lex
>>> x, y, z = symbols("x,y,z") >>> R, f = sring(x + 2*y + 3*z) >>> R Polynomial ring in x, y, z over ZZ with lex order >>> f x + 2*y + 3*z >>> type(_) <class 'sympy.polys.rings.PolyElement'>
""" single = False
if not is_sequence(exprs): exprs, single = [exprs], True
exprs = list(map(sympify, exprs)) opt = build_options(symbols, options)
# TODO: rewrite this so that it doesn't use expand() (see poly()). reps, opt = _parallel_dict_from_expr(exprs, opt)
if opt.domain is None: # NOTE: this is inefficient because construct_domain() automatically # performs conversion to the target domain. It shouldn't do this. coeffs = sum([ list(rep.values()) for rep in reps ], []) opt.domain, _ = construct_domain(coeffs, opt=opt)
_ring = PolyRing(opt.gens, opt.domain, opt.order) polys = list(map(_ring.from_dict, reps))
if single: return (_ring, polys[0]) else: return (_ring, polys)
def _parse_symbols(symbols): raise GeneratorsNeeded("generators weren't specified")
return (symbols,) return _symbols(symbols)
raise GeneratorsError("expected a string, Symbol or expression or a non-empty sequence of strings, Symbols or expressions")
_ring_cache = {}
class PolyRing(DefaultPrinting, IPolys): """Multivariate distributed polynomial ring. """
def __new__(cls, symbols, domain, order=lex):
raise GeneratorsError("polynomial ring and it's ground domain share generators")
else: obj.leading_expv = lambda f: max(f, key=order)
def _gens(self): """Return a list of polynomial generators. """
def __getnewargs__(self): return (self.symbols, self.domain, self.order)
def __getstate__(self): state = self.__dict__.copy() del state["leading_expv"]
for key, value in state.items(): if key.startswith("monomial_"): del state[key]
return state
def __hash__(self):
def __eq__(self, other):
def __ne__(self, other):
def clone(self, symbols=None, domain=None, order=None):
def monomial_basis(self, i): """Return the ith-basis element. """
@property def zero(self):
@property def one(self):
def domain_new(self, element, orig_domain=None):
def ground_new(self, coeff):
def term_new(self, monom, coeff):
def ring_new(self, element): if self == element.ring: return element elif isinstance(self.domain, PolynomialRing) and self.domain.ring == element.ring: return self.ground_new(element) else: raise NotImplementedError("conversion") raise NotImplementedError("parsing") try: return self.from_terms(element) except ValueError: return self.from_list(element) return self.from_expr(element) else:
__call__ = ring_new
def from_dict(self, element):
def from_terms(self, element):
def from_list(self, element): return self.from_dict(dmp_to_dict(element, self.ngens-1, self.domain))
def _rebuild_expr(self, expr, mapping): domain = self.domain
def _rebuild(expr): generator = mapping.get(expr)
if generator is not None: return generator elif expr.is_Add: return reduce(add, list(map(_rebuild, expr.args))) elif expr.is_Mul: return reduce(mul, list(map(_rebuild, expr.args))) elif expr.is_Pow and expr.exp.is_Integer and expr.exp >= 0: return _rebuild(expr.base)**int(expr.exp) else: return domain.convert(expr)
return _rebuild(sympify(expr))
def from_expr(self, expr): mapping = dict(list(zip(self.symbols, self.gens)))
try: poly = self._rebuild_expr(expr, mapping) except CoercionFailed: raise ValueError("expected an expression convertible to a polynomial in %s, got %s" % (self, expr)) else: return self.ring_new(poly)
def index(self, gen): """Compute index of ``gen`` in ``self.gens``. """ i = gen
if 0 <= i and i < self.ngens: pass elif -self.ngens <= i and i <= -1: i = -i - 1 else: raise ValueError("invalid generator index: %s" % gen) except ValueError: raise ValueError("invalid generator: %s" % gen) elif isinstance(gen, string_types): try: i = self.symbols.index(gen) except ValueError: raise ValueError("invalid generator: %s" % gen) else: raise ValueError("expected a polynomial generator, an integer, a string or None, got %s" % gen)
def drop(self, *gens): """Remove specified generators from this ring. """
return self.domain else:
def __getitem__(self, key): symbols = self.symbols[key]
if not symbols: return self.domain else: return self.clone(symbols=symbols)
def to_ground(self): # TODO: should AlgebraicField be a Composite domain? if self.domain.is_Composite or hasattr(self.domain, 'domain'): return self.clone(domain=self.domain.domain) else: raise ValueError("%s is not a composite domain" % self.domain)
def to_domain(self):
def to_field(self):
@property def is_univariate(self): return len(self.gens) == 1
@property def is_multivariate(self): return len(self.gens) > 1
def add(self, *objs): """ Add a sequence of polynomials or containers of polynomials.
Examples ========
>>> from sympy.polys.rings import ring >>> from sympy.polys.domains import ZZ
>>> R, x = ring("x", ZZ) >>> R.add([ x**2 + 2*i + 3 for i in range(4) ]) 4*x**2 + 24 >>> _.factor_list() (4, [(x**2 + 6, 1)])
""" p = self.zero
for obj in objs: if is_sequence(obj, include=GeneratorType): p += self.add(*obj) else: p += obj
return p
def mul(self, *objs): """ Multiply a sequence of polynomials or containers of polynomials.
Examples ========
>>> from sympy.polys.rings import ring >>> from sympy.polys.domains import ZZ
>>> R, x = ring("x", ZZ) >>> R.mul([ x**2 + 2*i + 3 for i in range(4) ]) x**8 + 24*x**6 + 206*x**4 + 744*x**2 + 945 >>> _.factor_list() (1, [(x**2 + 3, 1), (x**2 + 5, 1), (x**2 + 7, 1), (x**2 + 9, 1)])
""" p = self.one
for obj in objs: if is_sequence(obj, include=GeneratorType): p *= self.mul(*obj) else: p *= obj
return p
def drop_to_ground(self, *gens): r""" Remove specified generators from the ring and inject them into its domain. """ indices = set(map(self.index, gens)) symbols = [s for i, s in enumerate(self.symbols) if i not in indices] gens = [gen for i, gen in enumerate(self.gens) if i not in indices]
if not symbols: return self else: return self.clone(symbols=symbols, domain=self.drop(*gens))
def compose(self, other): """Add the generators of ``other`` to ``self``""" if self != other: syms = set(self.symbols).union(set(other.symbols)) return self.clone(symbols=list(syms)) else: return self
def add_gens(self, symbols): """Add the elements of ``symbols`` as generators to ``self``""" syms = set(self.symbols).union(set(symbols)) return self.clone(symbols=list(syms))
class PolyElement(DomainElement, DefaultPrinting, CantSympify, dict): """Element of multivariate distributed polynomial ring. """
def new(self, init):
def parent(self):
def __getnewargs__(self): return (self.ring, list(self.iterterms()))
_hash = None
def __hash__(self): # XXX: This computes a hash of a dictionary, but currently we don't # protect dictionary from being changed so any use site modifications # will make hashing go wrong. Use this feature with caution until we # figure out how to make a safe API without compromising speed of this # low-level class.
def copy(self): """Return a copy of polynomial self.
Polynomials are mutable; if one is interested in preserving a polynomial, and one plans to use inplace operations, one can copy the polynomial. This method makes a shallow copy.
Examples ========
>>> from sympy.polys.domains import ZZ >>> from sympy.polys.rings import ring
>>> R, x, y = ring('x, y', ZZ) >>> p = (x + y)**2 >>> p1 = p.copy() >>> p2 = p >>> p[R.zero_monom] = 3 >>> p x**2 + 2*x*y + y**2 + 3 >>> p1 x**2 + 2*x*y + y**2 >>> p2 x**2 + 2*x*y + y**2 + 3
"""
def set_ring(self, new_ring): else:
def as_expr(self, *symbols): raise ValueError("not enough symbols, expected %s got %s" % (self.ring.ngens, len(symbols))) else:
def as_expr_dict(self):
def clear_denoms(self):
def strip_zero(self): """Eliminate monomials with zero coefficient. """
def __eq__(p1, p2): """Equality test for polynomials.
Examples ========
>>> from sympy.polys.domains import ZZ >>> from sympy.polys.rings import ring
>>> _, x, y = ring('x, y', ZZ) >>> p1 = (x + y)**2 + (x - y)**2 >>> p1 == 4*x*y False >>> p1 == 2*(x**2 + y**2) True
""" else:
def __ne__(p1, p2): return not p1.__eq__(p2)
def almosteq(p1, p2, tolerance=None): """Approximate equality test for polynomials. """ ring = p1.ring
if isinstance(p2, ring.dtype): if set(p1.keys()) != set(p2.keys()): return False
almosteq = ring.domain.almosteq
for k in p1.keys(): if not almosteq(p1[k], p2[k], tolerance): return False else: return True elif len(p1) > 1: return False else: try: p2 = ring.domain.convert(p2) except CoercionFailed: return False else: return ring.domain.almosteq(p1.const(), p2, tolerance)
def sort_key(self): return (len(self), self.terms())
def _cmp(p1, p2, op): if isinstance(p2, p1.ring.dtype): return op(p1.sort_key(), p2.sort_key()) else: return NotImplemented
def __lt__(p1, p2): return p1._cmp(p2, lt) def __le__(p1, p2): return p1._cmp(p2, le) def __gt__(p1, p2): return p1._cmp(p2, gt) def __ge__(p1, p2): return p1._cmp(p2, ge)
def _drop(self, gen): ring = self.ring i = ring.index(gen)
if ring.ngens == 1: return i, ring.domain else: symbols = list(ring.symbols) del symbols[i] return i, ring.clone(symbols=symbols)
def drop(self, gen): i, ring = self._drop(gen)
if self.ring.ngens == 1: if self.is_ground: return self.coeff(1) else: raise ValueError("can't drop %s" % gen) else: poly = ring.zero
for k, v in self.items(): if k[i] == 0: K = list(k) del K[i] poly[tuple(K)] = v else: raise ValueError("can't drop %s" % gen)
return poly
def _drop_to_ground(self, gen): ring = self.ring i = ring.index(gen)
symbols = list(ring.symbols) del symbols[i] return i, ring.clone(symbols=symbols, domain=ring[i])
def drop_to_ground(self, gen): if self.ring.ngens == 1: raise ValueError("can't drop only generator to ground")
i, ring = self._drop_to_ground(gen) poly = ring.zero gen = ring.domain.gens[0]
for monom, coeff in self.iterterms(): mon = monom[:i] + monom[i+1:] if not mon in poly: poly[mon] = (gen**monom[i]).mul_ground(coeff) else: poly[mon] += (gen**monom[i]).mul_ground(coeff)
return poly
def to_dense(self): return dmp_from_dict(self, self.ring.ngens-1, self.ring.domain)
def to_dict(self):
def str(self, printer, precedence, exp_pattern, mul_symbol): if not self: return printer._print(self.ring.domain.zero) prec_add = precedence["Add"] prec_mul = precedence["Mul"] prec_atom = precedence["Atom"] ring = self.ring symbols = ring.symbols ngens = ring.ngens zm = ring.zero_monom sexpvs = [] for expv, coeff in self.terms(): positive = ring.domain.is_positive(coeff) sign = " + " if positive else " - " sexpvs.append(sign) if expv == zm: scoeff = printer._print(coeff) if scoeff.startswith("-"): scoeff = scoeff[1:] else: if not positive: coeff = -coeff if coeff != 1: scoeff = printer.parenthesize(coeff, prec_mul, strict=True) else: scoeff = '' sexpv = [] for i in range(ngens): exp = expv[i] if not exp: continue symbol = printer.parenthesize(symbols[i], prec_atom, strict=True) if exp != 1: if exp != int(exp) or exp < 0: sexp = printer.parenthesize(exp, prec_atom, strict=False) else: sexp = exp sexpv.append(exp_pattern % (symbol, sexp)) else: sexpv.append('%s' % symbol) if scoeff: sexpv = [scoeff] + sexpv sexpvs.append(mul_symbol.join(sexpv)) if sexpvs[0] in [" + ", " - "]: head = sexpvs.pop(0) if head == " - ": sexpvs.insert(0, "-") return "".join(sexpvs)
@property def is_generator(self): return self in self.ring._gens_set
@property def is_ground(self):
@property def is_monomial(self):
@property def is_term(self):
@property def is_negative(self):
@property def is_positive(self): return self.ring.domain.is_positive(self.LC)
@property def is_nonnegative(self):
@property def is_nonpositive(self): return self.ring.domain.is_nonpositive(self.LC)
@property def is_zero(f): return not f
@property def is_one(f): return f == f.ring.one
@property def is_monic(f): return f.ring.domain.is_one(f.LC)
@property def is_primitive(f): return f.ring.domain.is_one(f.content())
@property def is_linear(f): return all(sum(monom) <= 1 for monom in f.itermonoms())
@property def is_quadratic(f): return all(sum(monom) <= 2 for monom in f.itermonoms())
@property def is_squarefree(f): return f.ring.dmp_sqf_p(f)
@property def is_irreducible(f): return f.ring.dmp_irreducible_p(f)
@property def is_cyclotomic(f): if f.ring.is_univariate: return f.ring.dup_cyclotomic_p(f) else: raise MultivariatePolynomialError("cyclotomic polynomial")
def __neg__(self):
def __pos__(self): return self
def __add__(p1, p2): """Add two polynomials.
Examples ========
>>> from sympy.polys.domains import ZZ >>> from sympy.polys.rings import ring
>>> _, x, y = ring('x, y', ZZ) >>> (x + y)**2 + (x - y)**2 2*x**2 + 2*y**2
""" else: if isinstance(ring.domain, PolynomialRing) and ring.domain.ring == p2.ring: pass elif isinstance(p2.ring.domain, PolynomialRing) and p2.ring.domain.ring == ring: return p2.__radd__(p1) else: return NotImplemented
except CoercionFailed: return NotImplemented else: return p else: else: p[zm] += cp2
def __radd__(p1, n): p = p1.copy() if not n: return p ring = p1.ring try: n = ring.domain_new(n) except CoercionFailed: return NotImplemented else: zm = ring.zero_monom if zm not in p1.keys(): p[zm] = n else: if n == -p[zm]: del p[zm] else: p[zm] += n return p
def __sub__(p1, p2): """Subtract polynomial p2 from p1.
Examples ========
>>> from sympy.polys.domains import ZZ >>> from sympy.polys.rings import ring
>>> _, x, y = ring('x, y', ZZ) >>> p1 = x + y**2 >>> p2 = x*y + y**2 >>> p1 - p2 -x*y + x
""" else: if isinstance(ring.domain, PolynomialRing) and ring.domain.ring == p2.ring: pass elif isinstance(p2.ring.domain, PolynomialRing) and p2.ring.domain.ring == ring: return p2.__rsub__(p1) else: return NotImplemented
except CoercionFailed: return NotImplemented else: p[zm] = -p2 else: else: p[zm] -= p2
def __rsub__(p1, n): """n - p1 with n convertible to the coefficient domain.
Examples ========
>>> from sympy.polys.domains import ZZ >>> from sympy.polys.rings import ring
>>> _, x, y = ring('x, y', ZZ) >>> p = x + y >>> 4 - p -x - y + 4
""" except CoercionFailed: return NotImplemented else:
def __mul__(p1, p2): """Multiply two polynomials.
Examples ========
>>> from sympy.polys.domains import QQ >>> from sympy.polys.rings import ring
>>> _, x, y = ring('x, y', QQ) >>> p1 = x + y >>> p2 = x - y >>> p1*p2 x**2 - y**2
""" if isinstance(ring.domain, PolynomialRing) and ring.domain.ring == p2.ring: pass elif isinstance(p2.ring.domain, PolynomialRing) and p2.ring.domain.ring == ring: return p2.__rmul__(p1) else: return NotImplemented
except CoercionFailed: return NotImplemented else:
def __rmul__(p1, p2): """p2 * p1 with p2 in the coefficient domain of p1.
Examples ========
>>> from sympy.polys.domains import ZZ >>> from sympy.polys.rings import ring
>>> _, x, y = ring('x, y', ZZ) >>> p = x + y >>> 4 * p 4*x + 4*y
""" p = p1.ring.zero if not p2: return p try: p2 = p.ring.domain_new(p2) except CoercionFailed: return NotImplemented else: for exp1, v1 in p1.items(): v = p2*v1 if v: p[exp1] = v return p
def __pow__(self, n): """raise polynomial to power `n`
Examples ========
>>> from sympy.polys.domains import ZZ >>> from sympy.polys.rings import ring
>>> _, x, y = ring('x, y', ZZ) >>> p = x + y**2 >>> p**3 x**3 + 3*x**2*y**2 + 3*x*y**4 + y**6
"""
else: raise ValueError("0**0") else:
# For ring series, we need negative and rational exponent support only # with monomials. n = int(n) if n < 0: raise ValueError("Negative exponent")
elif n == 1: return self.copy() elif n == 2: return self.square() elif n == 3: return self*self.square() elif len(self) <= 5: # TODO: use an actuall density measure return self._pow_multinomial(n) else: return self._pow_generic(n)
def _pow_generic(self, n): p = self.ring.one c = self
while True: if n & 1: p = p*c n -= 1 if not n: break
c = c.square() n = n // 2
return p
def _pow_multinomial(self, n): multinomials = list(multinomial_coefficients(len(self), n).items()) monomial_mulpow = self.ring.monomial_mulpow zero_monom = self.ring.zero_monom terms = list(self.iterterms()) zero = self.ring.domain.zero poly = self.ring.zero
for multinomial, multinomial_coeff in multinomials: product_monom = zero_monom product_coeff = multinomial_coeff
for exp, (monom, coeff) in zip(multinomial, terms): if exp: product_monom = monomial_mulpow(product_monom, monom, exp) product_coeff *= coeff**exp
monom = tuple(product_monom) coeff = product_coeff
coeff = poly.get(monom, zero) + coeff
if coeff: poly[monom] = coeff else: del poly[monom]
return poly
def square(self): """square of a polynomial
Examples ========
>>> from sympy.polys.rings import ring >>> from sympy.polys.domains import ZZ
>>> _, x, y = ring('x, y', ZZ) >>> p = x + y**2 >>> p.square() x**2 + 2*x*y**2 + y**4
""" ring = self.ring p = ring.zero get = p.get keys = list(self.keys()) zero = ring.domain.zero monomial_mul = ring.monomial_mul for i in range(len(keys)): k1 = keys[i] pk = self[k1] for j in range(i): k2 = keys[j] exp = monomial_mul(k1, k2) p[exp] = get(exp, zero) + pk*self[k2] p = p.imul_num(2) get = p.get for k, v in self.items(): k2 = monomial_mul(k, k) p[k2] = get(k2, zero) + v**2 p.strip_zero() return p
def __divmod__(p1, p2): ring = p1.ring p = ring.zero
if not p2: raise ZeroDivisionError("polynomial division") elif isinstance(p2, ring.dtype): return p1.div(p2) elif isinstance(p2, PolyElement): if isinstance(ring.domain, PolynomialRing) and ring.domain.ring == p2.ring: pass elif isinstance(p2.ring.domain, PolynomialRing) and p2.ring.domain.ring == ring: return p2.__rdivmod__(p1) else: return NotImplemented
try: p2 = ring.domain_new(p2) except CoercionFailed: return NotImplemented else: return (p1.quo_ground(p2), p1.rem_ground(p2))
def __rdivmod__(p1, p2): return NotImplemented
def __mod__(p1, p2): ring = p1.ring p = ring.zero
if not p2: raise ZeroDivisionError("polynomial division") elif isinstance(p2, ring.dtype): return p1.rem(p2) elif isinstance(p2, PolyElement): if isinstance(ring.domain, PolynomialRing) and ring.domain.ring == p2.ring: pass elif isinstance(p2.ring.domain, PolynomialRing) and p2.ring.domain.ring == ring: return p2.__rmod__(p1) else: return NotImplemented
try: p2 = ring.domain_new(p2) except CoercionFailed: return NotImplemented else: return p1.rem_ground(p2)
def __rmod__(p1, p2): return NotImplemented
def __truediv__(p1, p2):
raise ZeroDivisionError("polynomial division") else: if isinstance(ring.domain, PolynomialRing) and ring.domain.ring == p2.ring: pass elif isinstance(p2.ring.domain, PolynomialRing) and p2.ring.domain.ring == ring: return p2.__rtruediv__(p1) else: return NotImplemented
except CoercionFailed: return NotImplemented else:
def __rtruediv__(p1, p2): return NotImplemented
__floordiv__ = __div__ = __truediv__ __rfloordiv__ = __rdiv__ = __rtruediv__
# TODO: use // (__floordiv__) for exquo()?
def _term_div(self):
else: else: else: else: else: return None
def div(self, fv): """Division algorithm, see [CLO] p64.
fv array of polynomials return qv, r such that self = sum(fv[i]*qv[i]) + r
All polynomials are required not to be Laurent polynomials.
Examples ========
>>> from sympy.polys.rings import ring >>> from sympy.polys.domains import ZZ
>>> _, x, y = ring('x, y', ZZ) >>> f = x**3 >>> f0 = x - y**2 >>> f1 = x - y >>> qv, r = f.div((f0, f1)) >>> qv[0] x**2 + x*y**2 + y**4 >>> qv[1] 0 >>> r y**6
""" raise ZeroDivisionError("polynomial division") if ret_single: return ring.zero, ring.zero else: return [], ring.zero raise ValueError('self and f must have the same ring') else: i += 1 expv = p.leading_expv() r = r._iadd_monom((expv, p[expv])) del p[expv] return ring.zero, r else: else: return qv, r
def rem(self, G): G = [G] raise ZeroDivisionError("polynomial division") else:
else: r[ltm] += ltc else:
def quo(f, G):
def exquo(f, G): q, r = f.div(G)
if not r: return q else: raise ExactQuotientFailed(f, G)
def _iadd_monom(self, mc): """add to self the monomial coeff*x0**i0*x1**i1*... unless self is a generator -- then just return the sum of the two.
mc is a tuple, (monom, coeff), where monomial is (i0, i1, ...)
Examples ========
>>> from sympy.polys.rings import ring >>> from sympy.polys.domains import ZZ
>>> _, x, y = ring('x, y', ZZ) >>> p = x**4 + 2*y >>> m = (1, 2) >>> p1 = p._iadd_monom((m, 5)) >>> p1 x**4 + 5*x*y**2 + 2*y >>> p1 is p True >>> p = x >>> p1 = p._iadd_monom((m, 5)) >>> p1 5*x*y**2 + x >>> p1 is p False
""" cpself = self.copy() else: else: c += coeff if c: cpself[expv] = c else: del cpself[expv]
def _iadd_poly_monom(self, p2, mc): """add to self the product of (p)*(coeff*x0**i0*x1**i1*...) unless self is a generator -- then just return the sum of the two.
mc is a tuple, (monom, coeff), where monomial is (i0, i1, ...)
Examples ========
>>> from sympy.polys.rings import ring >>> from sympy.polys.domains import ZZ
>>> _, x, y, z = ring('x, y, z', ZZ) >>> p1 = x**4 + 2*y >>> p2 = y + z >>> m = (1, 2, 3) >>> p1 = p1._iadd_poly_monom(p2, (m, 3)) >>> p1 x**4 + 3*x*y**3*z**3 + 3*x*y**2*z**4 + 2*y
""" p1[ka] = coeff else:
def degree(f, x=None): """ The leading degree in ``x`` or the main variable.
Note that the degree of 0 is negative infinity (the SymPy object -oo).
"""
return -oo else:
def degrees(f): """ A tuple containing leading degrees in all variables.
Note that the degree of 0 is negative infinity (the SymPy object -oo)
""" if not f: return (-oo,)*f.ring.ngens else: return tuple(map(max, list(zip(*f.itermonoms()))))
def tail_degree(f, x=None): """ The tail degree in ``x`` or the main variable.
Note that the degree of 0 is negative infinity (the SymPy object -oo)
""" i = f.ring.index(x)
if not f: return -oo else: return min([ monom[i] for monom in f.itermonoms() ])
def tail_degrees(f): """ A tuple containing tail degrees in all variables.
Note that the degree of 0 is negative infinity (the SymPy object -oo)
""" if not f: return (-oo,)*f.ring.ngens else: return tuple(map(min, list(zip(*f.itermonoms()))))
def leading_expv(self): """Leading monomial tuple according to the monomial ordering.
Examples ========
>>> from sympy.polys.rings import ring >>> from sympy.polys.domains import ZZ
>>> _, x, y, z = ring('x, y, z', ZZ) >>> p = x**4 + x**3*y + x**2*z**2 + z**7 >>> p.leading_expv() (4, 0, 0)
""" else:
def _get_coeff(self, expv):
def coeff(self, element): """ Returns the coefficient that stands next to the given monomial.
Parameters ---------- element : PolyElement (with ``is_monomial = True``) or 1
Examples ========
>>> from sympy.polys.rings import ring >>> from sympy.polys.domains import ZZ
>>> _, x, y, z = ring("x,y,z", ZZ) >>> f = 3*x**2*y - x*y*z + 7*z**3 + 23
>>> f.coeff(x**2*y) 3 >>> f.coeff(x*y) 0 >>> f.coeff(1) 23
""" if element == 1: return self._get_coeff(self.ring.zero_monom) elif isinstance(element, self.ring.dtype): terms = list(element.iterterms()) if len(terms) == 1: monom, coeff = terms[0] if coeff == self.ring.domain.one: return self._get_coeff(monom)
raise ValueError("expected a monomial, got %s" % element)
def const(self): """Returns the constant coeffcient. """ return self._get_coeff(self.ring.zero_monom)
@property def LC(self):
@property def LM(self): return self.ring.zero_monom else:
def leading_monom(self): """ Leading monomial as a polynomial element.
Examples ========
>>> from sympy.polys.rings import ring >>> from sympy.polys.domains import ZZ
>>> _, x, y = ring('x, y', ZZ) >>> (3*x*y + y**2).leading_monom() x*y
""" p = self.ring.zero expv = self.leading_expv() if expv: p[expv] = self.ring.domain.one return p
@property def LT(self): else:
def leading_term(self): """Leading term as a polynomial element.
Examples ========
>>> from sympy.polys.rings import ring >>> from sympy.polys.domains import ZZ
>>> _, x, y = ring('x, y', ZZ) >>> (3*x*y + y**2).leading_term() 3*x*y
""" p = self.ring.zero expv = self.leading_expv() if expv: p[expv] = self[expv] return p
def _sorted(self, seq, order): if order is None: order = self.ring.order else: order = OrderOpt.preprocess(order)
if order is lex: return sorted(seq, key=lambda monom: monom[0], reverse=True) else: return sorted(seq, key=lambda monom: order(monom[0]), reverse=True)
def coeffs(self, order=None): """Ordered list of polynomial coefficients.
Parameters ---------- order : :class:`Order` or coercible, optional
Examples ========
>>> from sympy.polys.rings import ring >>> from sympy.polys.domains import ZZ >>> from sympy.polys.orderings import lex, grlex
>>> _, x, y = ring("x, y", ZZ, lex) >>> f = x*y**7 + 2*x**2*y**3
>>> f.coeffs() [2, 1] >>> f.coeffs(grlex) [1, 2]
""" return [ coeff for _, coeff in self.terms(order) ]
def monoms(self, order=None): """Ordered list of polynomial monomials.
Parameters ---------- order : :class:`Order` or coercible, optional
Examples ========
>>> from sympy.polys.rings import ring >>> from sympy.polys.domains import ZZ >>> from sympy.polys.orderings import lex, grlex
>>> _, x, y = ring("x, y", ZZ, lex) >>> f = x*y**7 + 2*x**2*y**3
>>> f.monoms() [(2, 3), (1, 7)] >>> f.monoms(grlex) [(1, 7), (2, 3)]
""" return [ monom for monom, _ in self.terms(order) ]
def terms(self, order=None): """Ordered list of polynomial terms.
Parameters ---------- order : :class:`Order` or coercible, optional
Examples ========
>>> from sympy.polys.rings import ring >>> from sympy.polys.domains import ZZ >>> from sympy.polys.orderings import lex, grlex
>>> _, x, y = ring("x, y", ZZ, lex) >>> f = x*y**7 + 2*x**2*y**3
>>> f.terms() [((2, 3), 2), ((1, 7), 1)] >>> f.terms(grlex) [((1, 7), 1), ((2, 3), 2)]
""" return self._sorted(list(self.items()), order)
def itercoeffs(self): """Iterator over coefficients of a polynomial. """
def itermonoms(self): """Iterator over monomials of a polynomial. """
def iterterms(self): """Iterator over terms of a polynomial. """
def listcoeffs(self): """Unordered list of polynomial coefficients. """
def listmonoms(self): """Unordered list of polynomial monomials. """
def listterms(self): """Unordered list of polynomial terms. """ return list(self.items())
def imul_num(p, c): """multiply inplace the polynomial p by an element in the coefficient ring, provided p is not one of the generators; else multiply not inplace
Examples ========
>>> from sympy.polys.rings import ring >>> from sympy.polys.domains import ZZ
>>> _, x, y = ring('x, y', ZZ) >>> p = x + y**2 >>> p1 = p.imul_num(3) >>> p1 3*x + 3*y**2 >>> p1 is p True >>> p = x >>> p1 = p.imul_num(3) >>> p1 3*x >>> p1 is p False
""" if p in p.ring._gens_set: return p*c if not c: p.clear() return for exp in p: p[exp] *= c return p
def content(f): """Returns GCD of polynomial's coefficients. """
def primitive(f): """Returns content and a primitive polynomial. """
def monic(f): """Divides all coefficients by the leading coefficient. """ return f else:
def mul_ground(f, x): return f.ring.zero
def mul_monom(f, monom):
def mul_term(f, term): monom, coeff = term
if not f or not coeff: return f.ring.zero elif monom == f.ring.zero_monom: return f.mul_ground(coeff)
monomial_mul = f.ring.monomial_mul terms = [ (monomial_mul(f_monom, monom), f_coeff*coeff) for f_monom, f_coeff in f.items() ] return f.new(terms)
def quo_ground(f, x):
raise ZeroDivisionError('polynomial division')
else: terms = [ (monom, coeff // x) for monom, coeff in f.iterterms() if not (coeff % x) ]
def quo_term(f, term): monom, coeff = term
if not coeff: raise ZeroDivisionError("polynomial division") elif not f: return f.ring.zero elif monom == f.ring.zero_monom: return f.quo_ground(coeff)
term_div = f._term_div()
terms = [ term_div(t, term) for t in f.iterterms() ] return f.new([ t for t in terms if t is not None ])
def trunc_ground(f, p):
coeff = coeff - p
else: terms = [ (monom, coeff % p) for monom, coeff in f.iterterms() ]
rem_ground = trunc_ground
def extract_ground(self, g):
def _norm(f, norm_func): return f.ring.domain.zero else:
def max_norm(f):
def l1_norm(f): return f._norm(sum)
def deflate(f, *G):
return J, polys
def inflate(f, J):
def lcm(self, g):
else: return h.monic()
def gcd(f, g):
def cofactors(f, g): zero = f.ring.zero return zero, zero, zero
def _gcd_zero(f, g): else:
def _gcd_monom(f, g):
def _gcd(f, g):
return f._gcd_QQ(g) else: # TODO: don't use dense representation (port PRS algorithms) return ring.dmp_inner_gcd(f, g)
def _gcd_ZZ(f, g):
def _gcd_QQ(self, g): f = self ring = f.ring new_ring = ring.clone(domain=ring.domain.get_ring())
cf, f = f.clear_denoms() cg, g = g.clear_denoms()
f = f.set_ring(new_ring) g = g.set_ring(new_ring)
h, cff, cfg = f._gcd_ZZ(g)
h = h.set_ring(ring) c, h = h.LC, h.monic()
cff = cff.set_ring(ring).mul_ground(ring.domain.quo(c, cf)) cfg = cfg.set_ring(ring).mul_ground(ring.domain.quo(c, cg))
return h, cff, cfg
def cancel(self, g): """ Cancel common factors in a rational function ``f/g``.
Examples ========
>>> from sympy.polys import ring, ZZ >>> R, x,y = ring("x,y", ZZ)
>>> (2*x**2 - 2).cancel(x**2 - 2*x + 1) (2*x + 2, x - 1)
"""
else: new_ring = ring.clone(domain=domain.get_ring())
cq, f = f.clear_denoms() cp, g = g.clear_denoms()
f = f.set_ring(new_ring) g = g.set_ring(new_ring)
_, p, q = f.cofactors(g) _, cp, cq = new_ring.domain.cofactors(cp, cq)
p = p.set_ring(ring) q = q.set_ring(ring)
p_neg = p.is_negative q_neg = q.is_negative
if p_neg and q_neg: p, q = -p, -q elif p_neg: cp, p = -cp, -p elif q_neg: cp, q = -cp, -q
p = p.mul_ground(cp) q = q.mul_ground(cq)
def diff(f, x): """Computes partial derivative in ``x``.
Examples ========
>>> from sympy.polys.rings import ring >>> from sympy.polys.domains import ZZ
>>> _, x, y = ring("x,y", ZZ) >>> p = x + x**2*y**3 >>> p.diff(x) 2*x*y**3 + 1
"""
def __call__(f, *values): if 0 < len(values) <= f.ring.ngens: return f.evaluate(list(zip(f.ring.gens, values))) else: raise ValueError("expected at least 1 and at most %s values, got %s" % (f.ring.ngens, len(values)))
def evaluate(self, x, a=None):
(X, a), x = x[0], x[1:] f = f.evaluate(X, a)
if not x: return f else: x = [ (Y.drop(X), a) for (Y, a) in x ] return f.evaluate(x)
else:
coeff = coeff + poly[monom]
if coeff: poly[monom] = coeff else: del poly[monom] else:
def subs(self, x, a=None): f = self
if isinstance(x, list) and a is None: for X, a in x: f = f.subs(X, a) return f
ring = f.ring i = ring.index(x) a = ring.domain.convert(a)
if ring.ngens == 1: result = ring.domain.zero
for (n,), coeff in f.iterterms(): result += coeff*a**n
return ring.ground_new(result) else: poly = ring.zero
for monom, coeff in f.iterterms(): n, monom = monom[i], monom[:i] + (0,) + monom[i+1:] coeff = coeff*a**n
if monom in poly: coeff = coeff + poly[monom]
if coeff: poly[monom] = coeff else: del poly[monom] else: if coeff: poly[monom] = coeff
return poly
def compose(f, x, a=None): ring = f.ring poly = ring.zero gens_map = dict(list(zip(ring.gens, list(range(ring.ngens)))))
if a is not None: replacements = [(x, a)] else: if isinstance(x, list): replacements = list(x) elif isinstance(x, dict): replacements = sorted(list(x.items()), key=lambda k: gens_map[k[0]]) else: raise ValueError("expected a generator, value pair a sequence of such pairs")
for k, (x, g) in enumerate(replacements): replacements[k] = (gens_map[x], ring.ring_new(g))
for monom, coeff in f.iterterms(): monom = list(monom) subpoly = ring.one
for i, g in replacements: n, monom[i] = monom[i], 0 if n: subpoly *= g**n
subpoly = subpoly.mul_term((tuple(monom), coeff)) poly += subpoly
return poly
# TODO: following methods should point to polynomial # representation independent algorithm implementations.
def pdiv(f, g): return f.ring.dmp_pdiv(f, g)
def prem(f, g): return f.ring.dmp_prem(f, g)
def pquo(f, g): return f.ring.dmp_quo(f, g)
def pexquo(f, g): return f.ring.dmp_exquo(f, g)
def half_gcdex(f, g): return f.ring.dmp_half_gcdex(f, g)
def gcdex(f, g): return f.ring.dmp_gcdex(f, g)
def subresultants(f, g): return f.ring.dmp_subresultants(f, g)
def resultant(f, g): return f.ring.dmp_resultant(f, g)
def discriminant(f): return f.ring.dmp_discriminant(f)
def decompose(f): if f.ring.is_univariate: return f.ring.dup_decompose(f) else: raise MultivariatePolynomialError("polynomial decomposition")
def shift(f, a): if f.ring.is_univariate: return f.ring.dup_shift(f, a) else: raise MultivariatePolynomialError("polynomial shift")
def sturm(f): if f.ring.is_univariate: return f.ring.dup_sturm(f) else: raise MultivariatePolynomialError("sturm sequence")
def gff_list(f): return f.ring.dmp_gff_list(f)
def sqf_norm(f): return f.ring.dmp_sqf_norm(f)
def sqf_part(f): return f.ring.dmp_sqf_part(f)
def sqf_list(f, all=False): return f.ring.dmp_sqf_list(f, all=all)
def factor_list(f): return f.ring.dmp_factor_list(f) |