Coverage for /usr/lib/python3/dist-packages/sympy/polys/rings.py: 25%
1449 statements
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
1"""Sparse polynomial rings. """
3from __future__ import annotations
4from typing import Any
6from operator import add, mul, lt, le, gt, ge
7from functools import reduce
8from types import GeneratorType
10from sympy.core.expr import Expr
11from sympy.core.numbers import igcd, oo
12from sympy.core.symbol import Symbol, symbols as _symbols
13from sympy.core.sympify import CantSympify, sympify
14from sympy.ntheory.multinomial import multinomial_coefficients
15from sympy.polys.compatibility import IPolys
16from sympy.polys.constructor import construct_domain
17from sympy.polys.densebasic import dmp_to_dict, dmp_from_dict
18from sympy.polys.domains.domainelement import DomainElement
19from sympy.polys.domains.polynomialring import PolynomialRing
20from sympy.polys.heuristicgcd import heugcd
21from sympy.polys.monomials import MonomialOps
22from sympy.polys.orderings import lex
23from sympy.polys.polyerrors import (
24 CoercionFailed, GeneratorsError,
25 ExactQuotientFailed, MultivariatePolynomialError)
26from sympy.polys.polyoptions import (Domain as DomainOpt,
27 Order as OrderOpt, build_options)
28from sympy.polys.polyutils import (expr_from_dict, _dict_reorder,
29 _parallel_dict_from_expr)
30from sympy.printing.defaults import DefaultPrinting
31from sympy.utilities import public, subsets
32from sympy.utilities.iterables import is_sequence
33from sympy.utilities.magic import pollute
35@public
36def ring(symbols, domain, order=lex):
37 """Construct a polynomial ring returning ``(ring, x_1, ..., x_n)``.
39 Parameters
40 ==========
42 symbols : str
43 Symbol/Expr or sequence of str, Symbol/Expr (non-empty)
44 domain : :class:`~.Domain` or coercible
45 order : :class:`~.MonomialOrder` or coercible, optional, defaults to ``lex``
47 Examples
48 ========
50 >>> from sympy.polys.rings import ring
51 >>> from sympy.polys.domains import ZZ
52 >>> from sympy.polys.orderings import lex
54 >>> R, x, y, z = ring("x,y,z", ZZ, lex)
55 >>> R
56 Polynomial ring in x, y, z over ZZ with lex order
57 >>> x + y + z
58 x + y + z
59 >>> type(_)
60 <class 'sympy.polys.rings.PolyElement'>
62 """
63 _ring = PolyRing(symbols, domain, order)
64 return (_ring,) + _ring.gens
66@public
67def xring(symbols, domain, order=lex):
68 """Construct a polynomial ring returning ``(ring, (x_1, ..., x_n))``.
70 Parameters
71 ==========
73 symbols : str
74 Symbol/Expr or sequence of str, Symbol/Expr (non-empty)
75 domain : :class:`~.Domain` or coercible
76 order : :class:`~.MonomialOrder` or coercible, optional, defaults to ``lex``
78 Examples
79 ========
81 >>> from sympy.polys.rings import xring
82 >>> from sympy.polys.domains import ZZ
83 >>> from sympy.polys.orderings import lex
85 >>> R, (x, y, z) = xring("x,y,z", ZZ, lex)
86 >>> R
87 Polynomial ring in x, y, z over ZZ with lex order
88 >>> x + y + z
89 x + y + z
90 >>> type(_)
91 <class 'sympy.polys.rings.PolyElement'>
93 """
94 _ring = PolyRing(symbols, domain, order)
95 return (_ring, _ring.gens)
97@public
98def vring(symbols, domain, order=lex):
99 """Construct a polynomial ring and inject ``x_1, ..., x_n`` into the global namespace.
101 Parameters
102 ==========
104 symbols : str
105 Symbol/Expr or sequence of str, Symbol/Expr (non-empty)
106 domain : :class:`~.Domain` or coercible
107 order : :class:`~.MonomialOrder` or coercible, optional, defaults to ``lex``
109 Examples
110 ========
112 >>> from sympy.polys.rings import vring
113 >>> from sympy.polys.domains import ZZ
114 >>> from sympy.polys.orderings import lex
116 >>> vring("x,y,z", ZZ, lex)
117 Polynomial ring in x, y, z over ZZ with lex order
118 >>> x + y + z # noqa:
119 x + y + z
120 >>> type(_)
121 <class 'sympy.polys.rings.PolyElement'>
123 """
124 _ring = PolyRing(symbols, domain, order)
125 pollute([ sym.name for sym in _ring.symbols ], _ring.gens)
126 return _ring
128@public
129def sring(exprs, *symbols, **options):
130 """Construct a ring deriving generators and domain from options and input expressions.
132 Parameters
133 ==========
135 exprs : :class:`~.Expr` or sequence of :class:`~.Expr` (sympifiable)
136 symbols : sequence of :class:`~.Symbol`/:class:`~.Expr`
137 options : keyword arguments understood by :class:`~.Options`
139 Examples
140 ========
142 >>> from sympy import sring, symbols
144 >>> x, y, z = symbols("x,y,z")
145 >>> R, f = sring(x + 2*y + 3*z)
146 >>> R
147 Polynomial ring in x, y, z over ZZ with lex order
148 >>> f
149 x + 2*y + 3*z
150 >>> type(_)
151 <class 'sympy.polys.rings.PolyElement'>
153 """
154 single = False
156 if not is_sequence(exprs):
157 exprs, single = [exprs], True
159 exprs = list(map(sympify, exprs))
160 opt = build_options(symbols, options)
162 # TODO: rewrite this so that it doesn't use expand() (see poly()).
163 reps, opt = _parallel_dict_from_expr(exprs, opt)
165 if opt.domain is None:
166 coeffs = sum([ list(rep.values()) for rep in reps ], [])
168 opt.domain, coeffs_dom = construct_domain(coeffs, opt=opt)
170 coeff_map = dict(zip(coeffs, coeffs_dom))
171 reps = [{m: coeff_map[c] for m, c in rep.items()} for rep in reps]
173 _ring = PolyRing(opt.gens, opt.domain, opt.order)
174 polys = list(map(_ring.from_dict, reps))
176 if single:
177 return (_ring, polys[0])
178 else:
179 return (_ring, polys)
181def _parse_symbols(symbols):
182 if isinstance(symbols, str):
183 return _symbols(symbols, seq=True) if symbols else ()
184 elif isinstance(symbols, Expr):
185 return (symbols,)
186 elif is_sequence(symbols):
187 if all(isinstance(s, str) for s in symbols):
188 return _symbols(symbols)
189 elif all(isinstance(s, Expr) for s in symbols):
190 return symbols
192 raise GeneratorsError("expected a string, Symbol or expression or a non-empty sequence of strings, Symbols or expressions")
194_ring_cache: dict[Any, Any] = {}
196class PolyRing(DefaultPrinting, IPolys):
197 """Multivariate distributed polynomial ring. """
199 def __new__(cls, symbols, domain, order=lex):
200 symbols = tuple(_parse_symbols(symbols))
201 ngens = len(symbols)
202 domain = DomainOpt.preprocess(domain)
203 order = OrderOpt.preprocess(order)
205 _hash_tuple = (cls.__name__, symbols, ngens, domain, order)
206 obj = _ring_cache.get(_hash_tuple)
208 if obj is None:
209 if domain.is_Composite and set(symbols) & set(domain.symbols):
210 raise GeneratorsError("polynomial ring and it's ground domain share generators")
212 obj = object.__new__(cls)
213 obj._hash_tuple = _hash_tuple
214 obj._hash = hash(_hash_tuple)
215 obj.dtype = type("PolyElement", (PolyElement,), {"ring": obj})
216 obj.symbols = symbols
217 obj.ngens = ngens
218 obj.domain = domain
219 obj.order = order
221 obj.zero_monom = (0,)*ngens
222 obj.gens = obj._gens()
223 obj._gens_set = set(obj.gens)
225 obj._one = [(obj.zero_monom, domain.one)]
227 if ngens:
228 # These expect monomials in at least one variable
229 codegen = MonomialOps(ngens)
230 obj.monomial_mul = codegen.mul()
231 obj.monomial_pow = codegen.pow()
232 obj.monomial_mulpow = codegen.mulpow()
233 obj.monomial_ldiv = codegen.ldiv()
234 obj.monomial_div = codegen.div()
235 obj.monomial_lcm = codegen.lcm()
236 obj.monomial_gcd = codegen.gcd()
237 else:
238 monunit = lambda a, b: ()
239 obj.monomial_mul = monunit
240 obj.monomial_pow = monunit
241 obj.monomial_mulpow = lambda a, b, c: ()
242 obj.monomial_ldiv = monunit
243 obj.monomial_div = monunit
244 obj.monomial_lcm = monunit
245 obj.monomial_gcd = monunit
248 if order is lex:
249 obj.leading_expv = max
250 else:
251 obj.leading_expv = lambda f: max(f, key=order)
253 for symbol, generator in zip(obj.symbols, obj.gens):
254 if isinstance(symbol, Symbol):
255 name = symbol.name
257 if not hasattr(obj, name):
258 setattr(obj, name, generator)
260 _ring_cache[_hash_tuple] = obj
262 return obj
264 def _gens(self):
265 """Return a list of polynomial generators. """
266 one = self.domain.one
267 _gens = []
268 for i in range(self.ngens):
269 expv = self.monomial_basis(i)
270 poly = self.zero
271 poly[expv] = one
272 _gens.append(poly)
273 return tuple(_gens)
275 def __getnewargs__(self):
276 return (self.symbols, self.domain, self.order)
278 def __getstate__(self):
279 state = self.__dict__.copy()
280 del state["leading_expv"]
282 for key, value in state.items():
283 if key.startswith("monomial_"):
284 del state[key]
286 return state
288 def __hash__(self):
289 return self._hash
291 def __eq__(self, other):
292 return isinstance(other, PolyRing) and \
293 (self.symbols, self.domain, self.ngens, self.order) == \
294 (other.symbols, other.domain, other.ngens, other.order)
296 def __ne__(self, other):
297 return not self == other
299 def clone(self, symbols=None, domain=None, order=None):
300 return self.__class__(symbols or self.symbols, domain or self.domain, order or self.order)
302 def monomial_basis(self, i):
303 """Return the ith-basis element. """
304 basis = [0]*self.ngens
305 basis[i] = 1
306 return tuple(basis)
308 @property
309 def zero(self):
310 return self.dtype()
312 @property
313 def one(self):
314 return self.dtype(self._one)
316 def domain_new(self, element, orig_domain=None):
317 return self.domain.convert(element, orig_domain)
319 def ground_new(self, coeff):
320 return self.term_new(self.zero_monom, coeff)
322 def term_new(self, monom, coeff):
323 coeff = self.domain_new(coeff)
324 poly = self.zero
325 if coeff:
326 poly[monom] = coeff
327 return poly
329 def ring_new(self, element):
330 if isinstance(element, PolyElement):
331 if self == element.ring:
332 return element
333 elif isinstance(self.domain, PolynomialRing) and self.domain.ring == element.ring:
334 return self.ground_new(element)
335 else:
336 raise NotImplementedError("conversion")
337 elif isinstance(element, str):
338 raise NotImplementedError("parsing")
339 elif isinstance(element, dict):
340 return self.from_dict(element)
341 elif isinstance(element, list):
342 try:
343 return self.from_terms(element)
344 except ValueError:
345 return self.from_list(element)
346 elif isinstance(element, Expr):
347 return self.from_expr(element)
348 else:
349 return self.ground_new(element)
351 __call__ = ring_new
353 def from_dict(self, element, orig_domain=None):
354 domain_new = self.domain_new
355 poly = self.zero
357 for monom, coeff in element.items():
358 coeff = domain_new(coeff, orig_domain)
359 if coeff:
360 poly[monom] = coeff
362 return poly
364 def from_terms(self, element, orig_domain=None):
365 return self.from_dict(dict(element), orig_domain)
367 def from_list(self, element):
368 return self.from_dict(dmp_to_dict(element, self.ngens-1, self.domain))
370 def _rebuild_expr(self, expr, mapping):
371 domain = self.domain
373 def _rebuild(expr):
374 generator = mapping.get(expr)
376 if generator is not None:
377 return generator
378 elif expr.is_Add:
379 return reduce(add, list(map(_rebuild, expr.args)))
380 elif expr.is_Mul:
381 return reduce(mul, list(map(_rebuild, expr.args)))
382 else:
383 # XXX: Use as_base_exp() to handle Pow(x, n) and also exp(n)
384 # XXX: E can be a generator e.g. sring([exp(2)]) -> ZZ[E]
385 base, exp = expr.as_base_exp()
386 if exp.is_Integer and exp > 1:
387 return _rebuild(base)**int(exp)
388 else:
389 return self.ground_new(domain.convert(expr))
391 return _rebuild(sympify(expr))
393 def from_expr(self, expr):
394 mapping = dict(list(zip(self.symbols, self.gens)))
396 try:
397 poly = self._rebuild_expr(expr, mapping)
398 except CoercionFailed:
399 raise ValueError("expected an expression convertible to a polynomial in %s, got %s" % (self, expr))
400 else:
401 return self.ring_new(poly)
403 def index(self, gen):
404 """Compute index of ``gen`` in ``self.gens``. """
405 if gen is None:
406 if self.ngens:
407 i = 0
408 else:
409 i = -1 # indicate impossible choice
410 elif isinstance(gen, int):
411 i = gen
413 if 0 <= i and i < self.ngens:
414 pass
415 elif -self.ngens <= i and i <= -1:
416 i = -i - 1
417 else:
418 raise ValueError("invalid generator index: %s" % gen)
419 elif isinstance(gen, self.dtype):
420 try:
421 i = self.gens.index(gen)
422 except ValueError:
423 raise ValueError("invalid generator: %s" % gen)
424 elif isinstance(gen, str):
425 try:
426 i = self.symbols.index(gen)
427 except ValueError:
428 raise ValueError("invalid generator: %s" % gen)
429 else:
430 raise ValueError("expected a polynomial generator, an integer, a string or None, got %s" % gen)
432 return i
434 def drop(self, *gens):
435 """Remove specified generators from this ring. """
436 indices = set(map(self.index, gens))
437 symbols = [ s for i, s in enumerate(self.symbols) if i not in indices ]
439 if not symbols:
440 return self.domain
441 else:
442 return self.clone(symbols=symbols)
444 def __getitem__(self, key):
445 symbols = self.symbols[key]
447 if not symbols:
448 return self.domain
449 else:
450 return self.clone(symbols=symbols)
452 def to_ground(self):
453 # TODO: should AlgebraicField be a Composite domain?
454 if self.domain.is_Composite or hasattr(self.domain, 'domain'):
455 return self.clone(domain=self.domain.domain)
456 else:
457 raise ValueError("%s is not a composite domain" % self.domain)
459 def to_domain(self):
460 return PolynomialRing(self)
462 def to_field(self):
463 from sympy.polys.fields import FracField
464 return FracField(self.symbols, self.domain, self.order)
466 @property
467 def is_univariate(self):
468 return len(self.gens) == 1
470 @property
471 def is_multivariate(self):
472 return len(self.gens) > 1
474 def add(self, *objs):
475 """
476 Add a sequence of polynomials or containers of polynomials.
478 Examples
479 ========
481 >>> from sympy.polys.rings import ring
482 >>> from sympy.polys.domains import ZZ
484 >>> R, x = ring("x", ZZ)
485 >>> R.add([ x**2 + 2*i + 3 for i in range(4) ])
486 4*x**2 + 24
487 >>> _.factor_list()
488 (4, [(x**2 + 6, 1)])
490 """
491 p = self.zero
493 for obj in objs:
494 if is_sequence(obj, include=GeneratorType):
495 p += self.add(*obj)
496 else:
497 p += obj
499 return p
501 def mul(self, *objs):
502 """
503 Multiply a sequence of polynomials or containers of polynomials.
505 Examples
506 ========
508 >>> from sympy.polys.rings import ring
509 >>> from sympy.polys.domains import ZZ
511 >>> R, x = ring("x", ZZ)
512 >>> R.mul([ x**2 + 2*i + 3 for i in range(4) ])
513 x**8 + 24*x**6 + 206*x**4 + 744*x**2 + 945
514 >>> _.factor_list()
515 (1, [(x**2 + 3, 1), (x**2 + 5, 1), (x**2 + 7, 1), (x**2 + 9, 1)])
517 """
518 p = self.one
520 for obj in objs:
521 if is_sequence(obj, include=GeneratorType):
522 p *= self.mul(*obj)
523 else:
524 p *= obj
526 return p
528 def drop_to_ground(self, *gens):
529 r"""
530 Remove specified generators from the ring and inject them into
531 its domain.
532 """
533 indices = set(map(self.index, gens))
534 symbols = [s for i, s in enumerate(self.symbols) if i not in indices]
535 gens = [gen for i, gen in enumerate(self.gens) if i not in indices]
537 if not symbols:
538 return self
539 else:
540 return self.clone(symbols=symbols, domain=self.drop(*gens))
542 def compose(self, other):
543 """Add the generators of ``other`` to ``self``"""
544 if self != other:
545 syms = set(self.symbols).union(set(other.symbols))
546 return self.clone(symbols=list(syms))
547 else:
548 return self
550 def add_gens(self, symbols):
551 """Add the elements of ``symbols`` as generators to ``self``"""
552 syms = set(self.symbols).union(set(symbols))
553 return self.clone(symbols=list(syms))
555 def symmetric_poly(self, n):
556 """
557 Return the elementary symmetric polynomial of degree *n* over
558 this ring's generators.
559 """
560 if n < 0 or n > self.ngens:
561 raise ValueError("Cannot generate symmetric polynomial of order %s for %s" % (n, self.gens))
562 elif not n:
563 return self.one
564 else:
565 poly = self.zero
566 for s in subsets(range(self.ngens), int(n)):
567 monom = tuple(int(i in s) for i in range(self.ngens))
568 poly += self.term_new(monom, self.domain.one)
569 return poly
572class PolyElement(DomainElement, DefaultPrinting, CantSympify, dict):
573 """Element of multivariate distributed polynomial ring. """
575 def new(self, init):
576 return self.__class__(init)
578 def parent(self):
579 return self.ring.to_domain()
581 def __getnewargs__(self):
582 return (self.ring, list(self.iterterms()))
584 _hash = None
586 def __hash__(self):
587 # XXX: This computes a hash of a dictionary, but currently we don't
588 # protect dictionary from being changed so any use site modifications
589 # will make hashing go wrong. Use this feature with caution until we
590 # figure out how to make a safe API without compromising speed of this
591 # low-level class.
592 _hash = self._hash
593 if _hash is None:
594 self._hash = _hash = hash((self.ring, frozenset(self.items())))
595 return _hash
597 def copy(self):
598 """Return a copy of polynomial self.
600 Polynomials are mutable; if one is interested in preserving
601 a polynomial, and one plans to use inplace operations, one
602 can copy the polynomial. This method makes a shallow copy.
604 Examples
605 ========
607 >>> from sympy.polys.domains import ZZ
608 >>> from sympy.polys.rings import ring
610 >>> R, x, y = ring('x, y', ZZ)
611 >>> p = (x + y)**2
612 >>> p1 = p.copy()
613 >>> p2 = p
614 >>> p[R.zero_monom] = 3
615 >>> p
616 x**2 + 2*x*y + y**2 + 3
617 >>> p1
618 x**2 + 2*x*y + y**2
619 >>> p2
620 x**2 + 2*x*y + y**2 + 3
622 """
623 return self.new(self)
625 def set_ring(self, new_ring):
626 if self.ring == new_ring:
627 return self
628 elif self.ring.symbols != new_ring.symbols:
629 terms = list(zip(*_dict_reorder(self, self.ring.symbols, new_ring.symbols)))
630 return new_ring.from_terms(terms, self.ring.domain)
631 else:
632 return new_ring.from_dict(self, self.ring.domain)
634 def as_expr(self, *symbols):
635 if not symbols:
636 symbols = self.ring.symbols
637 elif len(symbols) != self.ring.ngens:
638 raise ValueError(
639 "Wrong number of symbols, expected %s got %s" %
640 (self.ring.ngens, len(symbols))
641 )
643 return expr_from_dict(self.as_expr_dict(), *symbols)
645 def as_expr_dict(self):
646 to_sympy = self.ring.domain.to_sympy
647 return {monom: to_sympy(coeff) for monom, coeff in self.iterterms()}
649 def clear_denoms(self):
650 domain = self.ring.domain
652 if not domain.is_Field or not domain.has_assoc_Ring:
653 return domain.one, self
655 ground_ring = domain.get_ring()
656 common = ground_ring.one
657 lcm = ground_ring.lcm
658 denom = domain.denom
660 for coeff in self.values():
661 common = lcm(common, denom(coeff))
663 poly = self.new([ (k, v*common) for k, v in self.items() ])
664 return common, poly
666 def strip_zero(self):
667 """Eliminate monomials with zero coefficient. """
668 for k, v in list(self.items()):
669 if not v:
670 del self[k]
672 def __eq__(p1, p2):
673 """Equality test for polynomials.
675 Examples
676 ========
678 >>> from sympy.polys.domains import ZZ
679 >>> from sympy.polys.rings import ring
681 >>> _, x, y = ring('x, y', ZZ)
682 >>> p1 = (x + y)**2 + (x - y)**2
683 >>> p1 == 4*x*y
684 False
685 >>> p1 == 2*(x**2 + y**2)
686 True
688 """
689 if not p2:
690 return not p1
691 elif isinstance(p2, PolyElement) and p2.ring == p1.ring:
692 return dict.__eq__(p1, p2)
693 elif len(p1) > 1:
694 return False
695 else:
696 return p1.get(p1.ring.zero_monom) == p2
698 def __ne__(p1, p2):
699 return not p1 == p2
701 def almosteq(p1, p2, tolerance=None):
702 """Approximate equality test for polynomials. """
703 ring = p1.ring
705 if isinstance(p2, ring.dtype):
706 if set(p1.keys()) != set(p2.keys()):
707 return False
709 almosteq = ring.domain.almosteq
711 for k in p1.keys():
712 if not almosteq(p1[k], p2[k], tolerance):
713 return False
714 return True
715 elif len(p1) > 1:
716 return False
717 else:
718 try:
719 p2 = ring.domain.convert(p2)
720 except CoercionFailed:
721 return False
722 else:
723 return ring.domain.almosteq(p1.const(), p2, tolerance)
725 def sort_key(self):
726 return (len(self), self.terms())
728 def _cmp(p1, p2, op):
729 if isinstance(p2, p1.ring.dtype):
730 return op(p1.sort_key(), p2.sort_key())
731 else:
732 return NotImplemented
734 def __lt__(p1, p2):
735 return p1._cmp(p2, lt)
736 def __le__(p1, p2):
737 return p1._cmp(p2, le)
738 def __gt__(p1, p2):
739 return p1._cmp(p2, gt)
740 def __ge__(p1, p2):
741 return p1._cmp(p2, ge)
743 def _drop(self, gen):
744 ring = self.ring
745 i = ring.index(gen)
747 if ring.ngens == 1:
748 return i, ring.domain
749 else:
750 symbols = list(ring.symbols)
751 del symbols[i]
752 return i, ring.clone(symbols=symbols)
754 def drop(self, gen):
755 i, ring = self._drop(gen)
757 if self.ring.ngens == 1:
758 if self.is_ground:
759 return self.coeff(1)
760 else:
761 raise ValueError("Cannot drop %s" % gen)
762 else:
763 poly = ring.zero
765 for k, v in self.items():
766 if k[i] == 0:
767 K = list(k)
768 del K[i]
769 poly[tuple(K)] = v
770 else:
771 raise ValueError("Cannot drop %s" % gen)
773 return poly
775 def _drop_to_ground(self, gen):
776 ring = self.ring
777 i = ring.index(gen)
779 symbols = list(ring.symbols)
780 del symbols[i]
781 return i, ring.clone(symbols=symbols, domain=ring[i])
783 def drop_to_ground(self, gen):
784 if self.ring.ngens == 1:
785 raise ValueError("Cannot drop only generator to ground")
787 i, ring = self._drop_to_ground(gen)
788 poly = ring.zero
789 gen = ring.domain.gens[0]
791 for monom, coeff in self.iterterms():
792 mon = monom[:i] + monom[i+1:]
793 if mon not in poly:
794 poly[mon] = (gen**monom[i]).mul_ground(coeff)
795 else:
796 poly[mon] += (gen**monom[i]).mul_ground(coeff)
798 return poly
800 def to_dense(self):
801 return dmp_from_dict(self, self.ring.ngens-1, self.ring.domain)
803 def to_dict(self):
804 return dict(self)
806 def str(self, printer, precedence, exp_pattern, mul_symbol):
807 if not self:
808 return printer._print(self.ring.domain.zero)
809 prec_mul = precedence["Mul"]
810 prec_atom = precedence["Atom"]
811 ring = self.ring
812 symbols = ring.symbols
813 ngens = ring.ngens
814 zm = ring.zero_monom
815 sexpvs = []
816 for expv, coeff in self.terms():
817 negative = ring.domain.is_negative(coeff)
818 sign = " - " if negative else " + "
819 sexpvs.append(sign)
820 if expv == zm:
821 scoeff = printer._print(coeff)
822 if negative and scoeff.startswith("-"):
823 scoeff = scoeff[1:]
824 else:
825 if negative:
826 coeff = -coeff
827 if coeff != self.ring.domain.one:
828 scoeff = printer.parenthesize(coeff, prec_mul, strict=True)
829 else:
830 scoeff = ''
831 sexpv = []
832 for i in range(ngens):
833 exp = expv[i]
834 if not exp:
835 continue
836 symbol = printer.parenthesize(symbols[i], prec_atom, strict=True)
837 if exp != 1:
838 if exp != int(exp) or exp < 0:
839 sexp = printer.parenthesize(exp, prec_atom, strict=False)
840 else:
841 sexp = exp
842 sexpv.append(exp_pattern % (symbol, sexp))
843 else:
844 sexpv.append('%s' % symbol)
845 if scoeff:
846 sexpv = [scoeff] + sexpv
847 sexpvs.append(mul_symbol.join(sexpv))
848 if sexpvs[0] in [" + ", " - "]:
849 head = sexpvs.pop(0)
850 if head == " - ":
851 sexpvs.insert(0, "-")
852 return "".join(sexpvs)
854 @property
855 def is_generator(self):
856 return self in self.ring._gens_set
858 @property
859 def is_ground(self):
860 return not self or (len(self) == 1 and self.ring.zero_monom in self)
862 @property
863 def is_monomial(self):
864 return not self or (len(self) == 1 and self.LC == 1)
866 @property
867 def is_term(self):
868 return len(self) <= 1
870 @property
871 def is_negative(self):
872 return self.ring.domain.is_negative(self.LC)
874 @property
875 def is_positive(self):
876 return self.ring.domain.is_positive(self.LC)
878 @property
879 def is_nonnegative(self):
880 return self.ring.domain.is_nonnegative(self.LC)
882 @property
883 def is_nonpositive(self):
884 return self.ring.domain.is_nonpositive(self.LC)
886 @property
887 def is_zero(f):
888 return not f
890 @property
891 def is_one(f):
892 return f == f.ring.one
894 @property
895 def is_monic(f):
896 return f.ring.domain.is_one(f.LC)
898 @property
899 def is_primitive(f):
900 return f.ring.domain.is_one(f.content())
902 @property
903 def is_linear(f):
904 return all(sum(monom) <= 1 for monom in f.itermonoms())
906 @property
907 def is_quadratic(f):
908 return all(sum(monom) <= 2 for monom in f.itermonoms())
910 @property
911 def is_squarefree(f):
912 if not f.ring.ngens:
913 return True
914 return f.ring.dmp_sqf_p(f)
916 @property
917 def is_irreducible(f):
918 if not f.ring.ngens:
919 return True
920 return f.ring.dmp_irreducible_p(f)
922 @property
923 def is_cyclotomic(f):
924 if f.ring.is_univariate:
925 return f.ring.dup_cyclotomic_p(f)
926 else:
927 raise MultivariatePolynomialError("cyclotomic polynomial")
929 def __neg__(self):
930 return self.new([ (monom, -coeff) for monom, coeff in self.iterterms() ])
932 def __pos__(self):
933 return self
935 def __add__(p1, p2):
936 """Add two polynomials.
938 Examples
939 ========
941 >>> from sympy.polys.domains import ZZ
942 >>> from sympy.polys.rings import ring
944 >>> _, x, y = ring('x, y', ZZ)
945 >>> (x + y)**2 + (x - y)**2
946 2*x**2 + 2*y**2
948 """
949 if not p2:
950 return p1.copy()
951 ring = p1.ring
952 if isinstance(p2, ring.dtype):
953 p = p1.copy()
954 get = p.get
955 zero = ring.domain.zero
956 for k, v in p2.items():
957 v = get(k, zero) + v
958 if v:
959 p[k] = v
960 else:
961 del p[k]
962 return p
963 elif isinstance(p2, PolyElement):
964 if isinstance(ring.domain, PolynomialRing) and ring.domain.ring == p2.ring:
965 pass
966 elif isinstance(p2.ring.domain, PolynomialRing) and p2.ring.domain.ring == ring:
967 return p2.__radd__(p1)
968 else:
969 return NotImplemented
971 try:
972 cp2 = ring.domain_new(p2)
973 except CoercionFailed:
974 return NotImplemented
975 else:
976 p = p1.copy()
977 if not cp2:
978 return p
979 zm = ring.zero_monom
980 if zm not in p1.keys():
981 p[zm] = cp2
982 else:
983 if p2 == -p[zm]:
984 del p[zm]
985 else:
986 p[zm] += cp2
987 return p
989 def __radd__(p1, n):
990 p = p1.copy()
991 if not n:
992 return p
993 ring = p1.ring
994 try:
995 n = ring.domain_new(n)
996 except CoercionFailed:
997 return NotImplemented
998 else:
999 zm = ring.zero_monom
1000 if zm not in p1.keys():
1001 p[zm] = n
1002 else:
1003 if n == -p[zm]:
1004 del p[zm]
1005 else:
1006 p[zm] += n
1007 return p
1009 def __sub__(p1, p2):
1010 """Subtract polynomial p2 from p1.
1012 Examples
1013 ========
1015 >>> from sympy.polys.domains import ZZ
1016 >>> from sympy.polys.rings import ring
1018 >>> _, x, y = ring('x, y', ZZ)
1019 >>> p1 = x + y**2
1020 >>> p2 = x*y + y**2
1021 >>> p1 - p2
1022 -x*y + x
1024 """
1025 if not p2:
1026 return p1.copy()
1027 ring = p1.ring
1028 if isinstance(p2, ring.dtype):
1029 p = p1.copy()
1030 get = p.get
1031 zero = ring.domain.zero
1032 for k, v in p2.items():
1033 v = get(k, zero) - v
1034 if v:
1035 p[k] = v
1036 else:
1037 del p[k]
1038 return p
1039 elif isinstance(p2, PolyElement):
1040 if isinstance(ring.domain, PolynomialRing) and ring.domain.ring == p2.ring:
1041 pass
1042 elif isinstance(p2.ring.domain, PolynomialRing) and p2.ring.domain.ring == ring:
1043 return p2.__rsub__(p1)
1044 else:
1045 return NotImplemented
1047 try:
1048 p2 = ring.domain_new(p2)
1049 except CoercionFailed:
1050 return NotImplemented
1051 else:
1052 p = p1.copy()
1053 zm = ring.zero_monom
1054 if zm not in p1.keys():
1055 p[zm] = -p2
1056 else:
1057 if p2 == p[zm]:
1058 del p[zm]
1059 else:
1060 p[zm] -= p2
1061 return p
1063 def __rsub__(p1, n):
1064 """n - p1 with n convertible to the coefficient domain.
1066 Examples
1067 ========
1069 >>> from sympy.polys.domains import ZZ
1070 >>> from sympy.polys.rings import ring
1072 >>> _, x, y = ring('x, y', ZZ)
1073 >>> p = x + y
1074 >>> 4 - p
1075 -x - y + 4
1077 """
1078 ring = p1.ring
1079 try:
1080 n = ring.domain_new(n)
1081 except CoercionFailed:
1082 return NotImplemented
1083 else:
1084 p = ring.zero
1085 for expv in p1:
1086 p[expv] = -p1[expv]
1087 p += n
1088 return p
1090 def __mul__(p1, p2):
1091 """Multiply two polynomials.
1093 Examples
1094 ========
1096 >>> from sympy.polys.domains import QQ
1097 >>> from sympy.polys.rings import ring
1099 >>> _, x, y = ring('x, y', QQ)
1100 >>> p1 = x + y
1101 >>> p2 = x - y
1102 >>> p1*p2
1103 x**2 - y**2
1105 """
1106 ring = p1.ring
1107 p = ring.zero
1108 if not p1 or not p2:
1109 return p
1110 elif isinstance(p2, ring.dtype):
1111 get = p.get
1112 zero = ring.domain.zero
1113 monomial_mul = ring.monomial_mul
1114 p2it = list(p2.items())
1115 for exp1, v1 in p1.items():
1116 for exp2, v2 in p2it:
1117 exp = monomial_mul(exp1, exp2)
1118 p[exp] = get(exp, zero) + v1*v2
1119 p.strip_zero()
1120 return p
1121 elif isinstance(p2, PolyElement):
1122 if isinstance(ring.domain, PolynomialRing) and ring.domain.ring == p2.ring:
1123 pass
1124 elif isinstance(p2.ring.domain, PolynomialRing) and p2.ring.domain.ring == ring:
1125 return p2.__rmul__(p1)
1126 else:
1127 return NotImplemented
1129 try:
1130 p2 = ring.domain_new(p2)
1131 except CoercionFailed:
1132 return NotImplemented
1133 else:
1134 for exp1, v1 in p1.items():
1135 v = v1*p2
1136 if v:
1137 p[exp1] = v
1138 return p
1140 def __rmul__(p1, p2):
1141 """p2 * p1 with p2 in the coefficient domain of p1.
1143 Examples
1144 ========
1146 >>> from sympy.polys.domains import ZZ
1147 >>> from sympy.polys.rings import ring
1149 >>> _, x, y = ring('x, y', ZZ)
1150 >>> p = x + y
1151 >>> 4 * p
1152 4*x + 4*y
1154 """
1155 p = p1.ring.zero
1156 if not p2:
1157 return p
1158 try:
1159 p2 = p.ring.domain_new(p2)
1160 except CoercionFailed:
1161 return NotImplemented
1162 else:
1163 for exp1, v1 in p1.items():
1164 v = p2*v1
1165 if v:
1166 p[exp1] = v
1167 return p
1169 def __pow__(self, n):
1170 """raise polynomial to power `n`
1172 Examples
1173 ========
1175 >>> from sympy.polys.domains import ZZ
1176 >>> from sympy.polys.rings import ring
1178 >>> _, x, y = ring('x, y', ZZ)
1179 >>> p = x + y**2
1180 >>> p**3
1181 x**3 + 3*x**2*y**2 + 3*x*y**4 + y**6
1183 """
1184 ring = self.ring
1186 if not n:
1187 if self:
1188 return ring.one
1189 else:
1190 raise ValueError("0**0")
1191 elif len(self) == 1:
1192 monom, coeff = list(self.items())[0]
1193 p = ring.zero
1194 if coeff == ring.domain.one:
1195 p[ring.monomial_pow(monom, n)] = coeff
1196 else:
1197 p[ring.monomial_pow(monom, n)] = coeff**n
1198 return p
1200 # For ring series, we need negative and rational exponent support only
1201 # with monomials.
1202 n = int(n)
1203 if n < 0:
1204 raise ValueError("Negative exponent")
1206 elif n == 1:
1207 return self.copy()
1208 elif n == 2:
1209 return self.square()
1210 elif n == 3:
1211 return self*self.square()
1212 elif len(self) <= 5: # TODO: use an actual density measure
1213 return self._pow_multinomial(n)
1214 else:
1215 return self._pow_generic(n)
1217 def _pow_generic(self, n):
1218 p = self.ring.one
1219 c = self
1221 while True:
1222 if n & 1:
1223 p = p*c
1224 n -= 1
1225 if not n:
1226 break
1228 c = c.square()
1229 n = n // 2
1231 return p
1233 def _pow_multinomial(self, n):
1234 multinomials = multinomial_coefficients(len(self), n).items()
1235 monomial_mulpow = self.ring.monomial_mulpow
1236 zero_monom = self.ring.zero_monom
1237 terms = self.items()
1238 zero = self.ring.domain.zero
1239 poly = self.ring.zero
1241 for multinomial, multinomial_coeff in multinomials:
1242 product_monom = zero_monom
1243 product_coeff = multinomial_coeff
1245 for exp, (monom, coeff) in zip(multinomial, terms):
1246 if exp:
1247 product_monom = monomial_mulpow(product_monom, monom, exp)
1248 product_coeff *= coeff**exp
1250 monom = tuple(product_monom)
1251 coeff = product_coeff
1253 coeff = poly.get(monom, zero) + coeff
1255 if coeff:
1256 poly[monom] = coeff
1257 elif monom in poly:
1258 del poly[monom]
1260 return poly
1262 def square(self):
1263 """square of a polynomial
1265 Examples
1266 ========
1268 >>> from sympy.polys.rings import ring
1269 >>> from sympy.polys.domains import ZZ
1271 >>> _, x, y = ring('x, y', ZZ)
1272 >>> p = x + y**2
1273 >>> p.square()
1274 x**2 + 2*x*y**2 + y**4
1276 """
1277 ring = self.ring
1278 p = ring.zero
1279 get = p.get
1280 keys = list(self.keys())
1281 zero = ring.domain.zero
1282 monomial_mul = ring.monomial_mul
1283 for i in range(len(keys)):
1284 k1 = keys[i]
1285 pk = self[k1]
1286 for j in range(i):
1287 k2 = keys[j]
1288 exp = monomial_mul(k1, k2)
1289 p[exp] = get(exp, zero) + pk*self[k2]
1290 p = p.imul_num(2)
1291 get = p.get
1292 for k, v in self.items():
1293 k2 = monomial_mul(k, k)
1294 p[k2] = get(k2, zero) + v**2
1295 p.strip_zero()
1296 return p
1298 def __divmod__(p1, p2):
1299 ring = p1.ring
1301 if not p2:
1302 raise ZeroDivisionError("polynomial division")
1303 elif isinstance(p2, ring.dtype):
1304 return p1.div(p2)
1305 elif isinstance(p2, PolyElement):
1306 if isinstance(ring.domain, PolynomialRing) and ring.domain.ring == p2.ring:
1307 pass
1308 elif isinstance(p2.ring.domain, PolynomialRing) and p2.ring.domain.ring == ring:
1309 return p2.__rdivmod__(p1)
1310 else:
1311 return NotImplemented
1313 try:
1314 p2 = ring.domain_new(p2)
1315 except CoercionFailed:
1316 return NotImplemented
1317 else:
1318 return (p1.quo_ground(p2), p1.rem_ground(p2))
1320 def __rdivmod__(p1, p2):
1321 return NotImplemented
1323 def __mod__(p1, p2):
1324 ring = p1.ring
1326 if not p2:
1327 raise ZeroDivisionError("polynomial division")
1328 elif isinstance(p2, ring.dtype):
1329 return p1.rem(p2)
1330 elif isinstance(p2, PolyElement):
1331 if isinstance(ring.domain, PolynomialRing) and ring.domain.ring == p2.ring:
1332 pass
1333 elif isinstance(p2.ring.domain, PolynomialRing) and p2.ring.domain.ring == ring:
1334 return p2.__rmod__(p1)
1335 else:
1336 return NotImplemented
1338 try:
1339 p2 = ring.domain_new(p2)
1340 except CoercionFailed:
1341 return NotImplemented
1342 else:
1343 return p1.rem_ground(p2)
1345 def __rmod__(p1, p2):
1346 return NotImplemented
1348 def __truediv__(p1, p2):
1349 ring = p1.ring
1351 if not p2:
1352 raise ZeroDivisionError("polynomial division")
1353 elif isinstance(p2, ring.dtype):
1354 if p2.is_monomial:
1355 return p1*(p2**(-1))
1356 else:
1357 return p1.quo(p2)
1358 elif isinstance(p2, PolyElement):
1359 if isinstance(ring.domain, PolynomialRing) and ring.domain.ring == p2.ring:
1360 pass
1361 elif isinstance(p2.ring.domain, PolynomialRing) and p2.ring.domain.ring == ring:
1362 return p2.__rtruediv__(p1)
1363 else:
1364 return NotImplemented
1366 try:
1367 p2 = ring.domain_new(p2)
1368 except CoercionFailed:
1369 return NotImplemented
1370 else:
1371 return p1.quo_ground(p2)
1373 def __rtruediv__(p1, p2):
1374 return NotImplemented
1376 __floordiv__ = __truediv__
1377 __rfloordiv__ = __rtruediv__
1379 # TODO: use // (__floordiv__) for exquo()?
1381 def _term_div(self):
1382 zm = self.ring.zero_monom
1383 domain = self.ring.domain
1384 domain_quo = domain.quo
1385 monomial_div = self.ring.monomial_div
1387 if domain.is_Field:
1388 def term_div(a_lm_a_lc, b_lm_b_lc):
1389 a_lm, a_lc = a_lm_a_lc
1390 b_lm, b_lc = b_lm_b_lc
1391 if b_lm == zm: # apparently this is a very common case
1392 monom = a_lm
1393 else:
1394 monom = monomial_div(a_lm, b_lm)
1395 if monom is not None:
1396 return monom, domain_quo(a_lc, b_lc)
1397 else:
1398 return None
1399 else:
1400 def term_div(a_lm_a_lc, b_lm_b_lc):
1401 a_lm, a_lc = a_lm_a_lc
1402 b_lm, b_lc = b_lm_b_lc
1403 if b_lm == zm: # apparently this is a very common case
1404 monom = a_lm
1405 else:
1406 monom = monomial_div(a_lm, b_lm)
1407 if not (monom is None or a_lc % b_lc):
1408 return monom, domain_quo(a_lc, b_lc)
1409 else:
1410 return None
1412 return term_div
1414 def div(self, fv):
1415 """Division algorithm, see [CLO] p64.
1417 fv array of polynomials
1418 return qv, r such that
1419 self = sum(fv[i]*qv[i]) + r
1421 All polynomials are required not to be Laurent polynomials.
1423 Examples
1424 ========
1426 >>> from sympy.polys.rings import ring
1427 >>> from sympy.polys.domains import ZZ
1429 >>> _, x, y = ring('x, y', ZZ)
1430 >>> f = x**3
1431 >>> f0 = x - y**2
1432 >>> f1 = x - y
1433 >>> qv, r = f.div((f0, f1))
1434 >>> qv[0]
1435 x**2 + x*y**2 + y**4
1436 >>> qv[1]
1437 0
1438 >>> r
1439 y**6
1441 """
1442 ring = self.ring
1443 ret_single = False
1444 if isinstance(fv, PolyElement):
1445 ret_single = True
1446 fv = [fv]
1447 if not all(fv):
1448 raise ZeroDivisionError("polynomial division")
1449 if not self:
1450 if ret_single:
1451 return ring.zero, ring.zero
1452 else:
1453 return [], ring.zero
1454 for f in fv:
1455 if f.ring != ring:
1456 raise ValueError('self and f must have the same ring')
1457 s = len(fv)
1458 qv = [ring.zero for i in range(s)]
1459 p = self.copy()
1460 r = ring.zero
1461 term_div = self._term_div()
1462 expvs = [fx.leading_expv() for fx in fv]
1463 while p:
1464 i = 0
1465 divoccurred = 0
1466 while i < s and divoccurred == 0:
1467 expv = p.leading_expv()
1468 term = term_div((expv, p[expv]), (expvs[i], fv[i][expvs[i]]))
1469 if term is not None:
1470 expv1, c = term
1471 qv[i] = qv[i]._iadd_monom((expv1, c))
1472 p = p._iadd_poly_monom(fv[i], (expv1, -c))
1473 divoccurred = 1
1474 else:
1475 i += 1
1476 if not divoccurred:
1477 expv = p.leading_expv()
1478 r = r._iadd_monom((expv, p[expv]))
1479 del p[expv]
1480 if expv == ring.zero_monom:
1481 r += p
1482 if ret_single:
1483 if not qv:
1484 return ring.zero, r
1485 else:
1486 return qv[0], r
1487 else:
1488 return qv, r
1490 def rem(self, G):
1491 f = self
1492 if isinstance(G, PolyElement):
1493 G = [G]
1494 if not all(G):
1495 raise ZeroDivisionError("polynomial division")
1496 ring = f.ring
1497 domain = ring.domain
1498 zero = domain.zero
1499 monomial_mul = ring.monomial_mul
1500 r = ring.zero
1501 term_div = f._term_div()
1502 ltf = f.LT
1503 f = f.copy()
1504 get = f.get
1505 while f:
1506 for g in G:
1507 tq = term_div(ltf, g.LT)
1508 if tq is not None:
1509 m, c = tq
1510 for mg, cg in g.iterterms():
1511 m1 = monomial_mul(mg, m)
1512 c1 = get(m1, zero) - c*cg
1513 if not c1:
1514 del f[m1]
1515 else:
1516 f[m1] = c1
1517 ltm = f.leading_expv()
1518 if ltm is not None:
1519 ltf = ltm, f[ltm]
1521 break
1522 else:
1523 ltm, ltc = ltf
1524 if ltm in r:
1525 r[ltm] += ltc
1526 else:
1527 r[ltm] = ltc
1528 del f[ltm]
1529 ltm = f.leading_expv()
1530 if ltm is not None:
1531 ltf = ltm, f[ltm]
1533 return r
1535 def quo(f, G):
1536 return f.div(G)[0]
1538 def exquo(f, G):
1539 q, r = f.div(G)
1541 if not r:
1542 return q
1543 else:
1544 raise ExactQuotientFailed(f, G)
1546 def _iadd_monom(self, mc):
1547 """add to self the monomial coeff*x0**i0*x1**i1*...
1548 unless self is a generator -- then just return the sum of the two.
1550 mc is a tuple, (monom, coeff), where monomial is (i0, i1, ...)
1552 Examples
1553 ========
1555 >>> from sympy.polys.rings import ring
1556 >>> from sympy.polys.domains import ZZ
1558 >>> _, x, y = ring('x, y', ZZ)
1559 >>> p = x**4 + 2*y
1560 >>> m = (1, 2)
1561 >>> p1 = p._iadd_monom((m, 5))
1562 >>> p1
1563 x**4 + 5*x*y**2 + 2*y
1564 >>> p1 is p
1565 True
1566 >>> p = x
1567 >>> p1 = p._iadd_monom((m, 5))
1568 >>> p1
1569 5*x*y**2 + x
1570 >>> p1 is p
1571 False
1573 """
1574 if self in self.ring._gens_set:
1575 cpself = self.copy()
1576 else:
1577 cpself = self
1578 expv, coeff = mc
1579 c = cpself.get(expv)
1580 if c is None:
1581 cpself[expv] = coeff
1582 else:
1583 c += coeff
1584 if c:
1585 cpself[expv] = c
1586 else:
1587 del cpself[expv]
1588 return cpself
1590 def _iadd_poly_monom(self, p2, mc):
1591 """add to self the product of (p)*(coeff*x0**i0*x1**i1*...)
1592 unless self is a generator -- then just return the sum of the two.
1594 mc is a tuple, (monom, coeff), where monomial is (i0, i1, ...)
1596 Examples
1597 ========
1599 >>> from sympy.polys.rings import ring
1600 >>> from sympy.polys.domains import ZZ
1602 >>> _, x, y, z = ring('x, y, z', ZZ)
1603 >>> p1 = x**4 + 2*y
1604 >>> p2 = y + z
1605 >>> m = (1, 2, 3)
1606 >>> p1 = p1._iadd_poly_monom(p2, (m, 3))
1607 >>> p1
1608 x**4 + 3*x*y**3*z**3 + 3*x*y**2*z**4 + 2*y
1610 """
1611 p1 = self
1612 if p1 in p1.ring._gens_set:
1613 p1 = p1.copy()
1614 (m, c) = mc
1615 get = p1.get
1616 zero = p1.ring.domain.zero
1617 monomial_mul = p1.ring.monomial_mul
1618 for k, v in p2.items():
1619 ka = monomial_mul(k, m)
1620 coeff = get(ka, zero) + v*c
1621 if coeff:
1622 p1[ka] = coeff
1623 else:
1624 del p1[ka]
1625 return p1
1627 def degree(f, x=None):
1628 """
1629 The leading degree in ``x`` or the main variable.
1631 Note that the degree of 0 is negative infinity (the SymPy object -oo).
1633 """
1634 i = f.ring.index(x)
1636 if not f:
1637 return -oo
1638 elif i < 0:
1639 return 0
1640 else:
1641 return max([ monom[i] for monom in f.itermonoms() ])
1643 def degrees(f):
1644 """
1645 A tuple containing leading degrees in all variables.
1647 Note that the degree of 0 is negative infinity (the SymPy object -oo)
1649 """
1650 if not f:
1651 return (-oo,)*f.ring.ngens
1652 else:
1653 return tuple(map(max, list(zip(*f.itermonoms()))))
1655 def tail_degree(f, x=None):
1656 """
1657 The tail degree in ``x`` or the main variable.
1659 Note that the degree of 0 is negative infinity (the SymPy object -oo)
1661 """
1662 i = f.ring.index(x)
1664 if not f:
1665 return -oo
1666 elif i < 0:
1667 return 0
1668 else:
1669 return min([ monom[i] for monom in f.itermonoms() ])
1671 def tail_degrees(f):
1672 """
1673 A tuple containing tail degrees in all variables.
1675 Note that the degree of 0 is negative infinity (the SymPy object -oo)
1677 """
1678 if not f:
1679 return (-oo,)*f.ring.ngens
1680 else:
1681 return tuple(map(min, list(zip(*f.itermonoms()))))
1683 def leading_expv(self):
1684 """Leading monomial tuple according to the monomial ordering.
1686 Examples
1687 ========
1689 >>> from sympy.polys.rings import ring
1690 >>> from sympy.polys.domains import ZZ
1692 >>> _, x, y, z = ring('x, y, z', ZZ)
1693 >>> p = x**4 + x**3*y + x**2*z**2 + z**7
1694 >>> p.leading_expv()
1695 (4, 0, 0)
1697 """
1698 if self:
1699 return self.ring.leading_expv(self)
1700 else:
1701 return None
1703 def _get_coeff(self, expv):
1704 return self.get(expv, self.ring.domain.zero)
1706 def coeff(self, element):
1707 """
1708 Returns the coefficient that stands next to the given monomial.
1710 Parameters
1711 ==========
1713 element : PolyElement (with ``is_monomial = True``) or 1
1715 Examples
1716 ========
1718 >>> from sympy.polys.rings import ring
1719 >>> from sympy.polys.domains import ZZ
1721 >>> _, x, y, z = ring("x,y,z", ZZ)
1722 >>> f = 3*x**2*y - x*y*z + 7*z**3 + 23
1724 >>> f.coeff(x**2*y)
1725 3
1726 >>> f.coeff(x*y)
1727 0
1728 >>> f.coeff(1)
1729 23
1731 """
1732 if element == 1:
1733 return self._get_coeff(self.ring.zero_monom)
1734 elif isinstance(element, self.ring.dtype):
1735 terms = list(element.iterterms())
1736 if len(terms) == 1:
1737 monom, coeff = terms[0]
1738 if coeff == self.ring.domain.one:
1739 return self._get_coeff(monom)
1741 raise ValueError("expected a monomial, got %s" % element)
1743 def const(self):
1744 """Returns the constant coefficient. """
1745 return self._get_coeff(self.ring.zero_monom)
1747 @property
1748 def LC(self):
1749 return self._get_coeff(self.leading_expv())
1751 @property
1752 def LM(self):
1753 expv = self.leading_expv()
1754 if expv is None:
1755 return self.ring.zero_monom
1756 else:
1757 return expv
1759 def leading_monom(self):
1760 """
1761 Leading monomial as a polynomial element.
1763 Examples
1764 ========
1766 >>> from sympy.polys.rings import ring
1767 >>> from sympy.polys.domains import ZZ
1769 >>> _, x, y = ring('x, y', ZZ)
1770 >>> (3*x*y + y**2).leading_monom()
1771 x*y
1773 """
1774 p = self.ring.zero
1775 expv = self.leading_expv()
1776 if expv:
1777 p[expv] = self.ring.domain.one
1778 return p
1780 @property
1781 def LT(self):
1782 expv = self.leading_expv()
1783 if expv is None:
1784 return (self.ring.zero_monom, self.ring.domain.zero)
1785 else:
1786 return (expv, self._get_coeff(expv))
1788 def leading_term(self):
1789 """Leading term as a polynomial element.
1791 Examples
1792 ========
1794 >>> from sympy.polys.rings import ring
1795 >>> from sympy.polys.domains import ZZ
1797 >>> _, x, y = ring('x, y', ZZ)
1798 >>> (3*x*y + y**2).leading_term()
1799 3*x*y
1801 """
1802 p = self.ring.zero
1803 expv = self.leading_expv()
1804 if expv is not None:
1805 p[expv] = self[expv]
1806 return p
1808 def _sorted(self, seq, order):
1809 if order is None:
1810 order = self.ring.order
1811 else:
1812 order = OrderOpt.preprocess(order)
1814 if order is lex:
1815 return sorted(seq, key=lambda monom: monom[0], reverse=True)
1816 else:
1817 return sorted(seq, key=lambda monom: order(monom[0]), reverse=True)
1819 def coeffs(self, order=None):
1820 """Ordered list of polynomial coefficients.
1822 Parameters
1823 ==========
1825 order : :class:`~.MonomialOrder` or coercible, optional
1827 Examples
1828 ========
1830 >>> from sympy.polys.rings import ring
1831 >>> from sympy.polys.domains import ZZ
1832 >>> from sympy.polys.orderings import lex, grlex
1834 >>> _, x, y = ring("x, y", ZZ, lex)
1835 >>> f = x*y**7 + 2*x**2*y**3
1837 >>> f.coeffs()
1838 [2, 1]
1839 >>> f.coeffs(grlex)
1840 [1, 2]
1842 """
1843 return [ coeff for _, coeff in self.terms(order) ]
1845 def monoms(self, order=None):
1846 """Ordered list of polynomial monomials.
1848 Parameters
1849 ==========
1851 order : :class:`~.MonomialOrder` or coercible, optional
1853 Examples
1854 ========
1856 >>> from sympy.polys.rings import ring
1857 >>> from sympy.polys.domains import ZZ
1858 >>> from sympy.polys.orderings import lex, grlex
1860 >>> _, x, y = ring("x, y", ZZ, lex)
1861 >>> f = x*y**7 + 2*x**2*y**3
1863 >>> f.monoms()
1864 [(2, 3), (1, 7)]
1865 >>> f.monoms(grlex)
1866 [(1, 7), (2, 3)]
1868 """
1869 return [ monom for monom, _ in self.terms(order) ]
1871 def terms(self, order=None):
1872 """Ordered list of polynomial terms.
1874 Parameters
1875 ==========
1877 order : :class:`~.MonomialOrder` or coercible, optional
1879 Examples
1880 ========
1882 >>> from sympy.polys.rings import ring
1883 >>> from sympy.polys.domains import ZZ
1884 >>> from sympy.polys.orderings import lex, grlex
1886 >>> _, x, y = ring("x, y", ZZ, lex)
1887 >>> f = x*y**7 + 2*x**2*y**3
1889 >>> f.terms()
1890 [((2, 3), 2), ((1, 7), 1)]
1891 >>> f.terms(grlex)
1892 [((1, 7), 1), ((2, 3), 2)]
1894 """
1895 return self._sorted(list(self.items()), order)
1897 def itercoeffs(self):
1898 """Iterator over coefficients of a polynomial. """
1899 return iter(self.values())
1901 def itermonoms(self):
1902 """Iterator over monomials of a polynomial. """
1903 return iter(self.keys())
1905 def iterterms(self):
1906 """Iterator over terms of a polynomial. """
1907 return iter(self.items())
1909 def listcoeffs(self):
1910 """Unordered list of polynomial coefficients. """
1911 return list(self.values())
1913 def listmonoms(self):
1914 """Unordered list of polynomial monomials. """
1915 return list(self.keys())
1917 def listterms(self):
1918 """Unordered list of polynomial terms. """
1919 return list(self.items())
1921 def imul_num(p, c):
1922 """multiply inplace the polynomial p by an element in the
1923 coefficient ring, provided p is not one of the generators;
1924 else multiply not inplace
1926 Examples
1927 ========
1929 >>> from sympy.polys.rings import ring
1930 >>> from sympy.polys.domains import ZZ
1932 >>> _, x, y = ring('x, y', ZZ)
1933 >>> p = x + y**2
1934 >>> p1 = p.imul_num(3)
1935 >>> p1
1936 3*x + 3*y**2
1937 >>> p1 is p
1938 True
1939 >>> p = x
1940 >>> p1 = p.imul_num(3)
1941 >>> p1
1942 3*x
1943 >>> p1 is p
1944 False
1946 """
1947 if p in p.ring._gens_set:
1948 return p*c
1949 if not c:
1950 p.clear()
1951 return
1952 for exp in p:
1953 p[exp] *= c
1954 return p
1956 def content(f):
1957 """Returns GCD of polynomial's coefficients. """
1958 domain = f.ring.domain
1959 cont = domain.zero
1960 gcd = domain.gcd
1962 for coeff in f.itercoeffs():
1963 cont = gcd(cont, coeff)
1965 return cont
1967 def primitive(f):
1968 """Returns content and a primitive polynomial. """
1969 cont = f.content()
1970 return cont, f.quo_ground(cont)
1972 def monic(f):
1973 """Divides all coefficients by the leading coefficient. """
1974 if not f:
1975 return f
1976 else:
1977 return f.quo_ground(f.LC)
1979 def mul_ground(f, x):
1980 if not x:
1981 return f.ring.zero
1983 terms = [ (monom, coeff*x) for monom, coeff in f.iterterms() ]
1984 return f.new(terms)
1986 def mul_monom(f, monom):
1987 monomial_mul = f.ring.monomial_mul
1988 terms = [ (monomial_mul(f_monom, monom), f_coeff) for f_monom, f_coeff in f.items() ]
1989 return f.new(terms)
1991 def mul_term(f, term):
1992 monom, coeff = term
1994 if not f or not coeff:
1995 return f.ring.zero
1996 elif monom == f.ring.zero_monom:
1997 return f.mul_ground(coeff)
1999 monomial_mul = f.ring.monomial_mul
2000 terms = [ (monomial_mul(f_monom, monom), f_coeff*coeff) for f_monom, f_coeff in f.items() ]
2001 return f.new(terms)
2003 def quo_ground(f, x):
2004 domain = f.ring.domain
2006 if not x:
2007 raise ZeroDivisionError('polynomial division')
2008 if not f or x == domain.one:
2009 return f
2011 if domain.is_Field:
2012 quo = domain.quo
2013 terms = [ (monom, quo(coeff, x)) for monom, coeff in f.iterterms() ]
2014 else:
2015 terms = [ (monom, coeff // x) for monom, coeff in f.iterterms() if not (coeff % x) ]
2017 return f.new(terms)
2019 def quo_term(f, term):
2020 monom, coeff = term
2022 if not coeff:
2023 raise ZeroDivisionError("polynomial division")
2024 elif not f:
2025 return f.ring.zero
2026 elif monom == f.ring.zero_monom:
2027 return f.quo_ground(coeff)
2029 term_div = f._term_div()
2031 terms = [ term_div(t, term) for t in f.iterterms() ]
2032 return f.new([ t for t in terms if t is not None ])
2034 def trunc_ground(f, p):
2035 if f.ring.domain.is_ZZ:
2036 terms = []
2038 for monom, coeff in f.iterterms():
2039 coeff = coeff % p
2041 if coeff > p // 2:
2042 coeff = coeff - p
2044 terms.append((monom, coeff))
2045 else:
2046 terms = [ (monom, coeff % p) for monom, coeff in f.iterterms() ]
2048 poly = f.new(terms)
2049 poly.strip_zero()
2050 return poly
2052 rem_ground = trunc_ground
2054 def extract_ground(self, g):
2055 f = self
2056 fc = f.content()
2057 gc = g.content()
2059 gcd = f.ring.domain.gcd(fc, gc)
2061 f = f.quo_ground(gcd)
2062 g = g.quo_ground(gcd)
2064 return gcd, f, g
2066 def _norm(f, norm_func):
2067 if not f:
2068 return f.ring.domain.zero
2069 else:
2070 ground_abs = f.ring.domain.abs
2071 return norm_func([ ground_abs(coeff) for coeff in f.itercoeffs() ])
2073 def max_norm(f):
2074 return f._norm(max)
2076 def l1_norm(f):
2077 return f._norm(sum)
2079 def deflate(f, *G):
2080 ring = f.ring
2081 polys = [f] + list(G)
2083 J = [0]*ring.ngens
2085 for p in polys:
2086 for monom in p.itermonoms():
2087 for i, m in enumerate(monom):
2088 J[i] = igcd(J[i], m)
2090 for i, b in enumerate(J):
2091 if not b:
2092 J[i] = 1
2094 J = tuple(J)
2096 if all(b == 1 for b in J):
2097 return J, polys
2099 H = []
2101 for p in polys:
2102 h = ring.zero
2104 for I, coeff in p.iterterms():
2105 N = [ i // j for i, j in zip(I, J) ]
2106 h[tuple(N)] = coeff
2108 H.append(h)
2110 return J, H
2112 def inflate(f, J):
2113 poly = f.ring.zero
2115 for I, coeff in f.iterterms():
2116 N = [ i*j for i, j in zip(I, J) ]
2117 poly[tuple(N)] = coeff
2119 return poly
2121 def lcm(self, g):
2122 f = self
2123 domain = f.ring.domain
2125 if not domain.is_Field:
2126 fc, f = f.primitive()
2127 gc, g = g.primitive()
2128 c = domain.lcm(fc, gc)
2130 h = (f*g).quo(f.gcd(g))
2132 if not domain.is_Field:
2133 return h.mul_ground(c)
2134 else:
2135 return h.monic()
2137 def gcd(f, g):
2138 return f.cofactors(g)[0]
2140 def cofactors(f, g):
2141 if not f and not g:
2142 zero = f.ring.zero
2143 return zero, zero, zero
2144 elif not f:
2145 h, cff, cfg = f._gcd_zero(g)
2146 return h, cff, cfg
2147 elif not g:
2148 h, cfg, cff = g._gcd_zero(f)
2149 return h, cff, cfg
2150 elif len(f) == 1:
2151 h, cff, cfg = f._gcd_monom(g)
2152 return h, cff, cfg
2153 elif len(g) == 1:
2154 h, cfg, cff = g._gcd_monom(f)
2155 return h, cff, cfg
2157 J, (f, g) = f.deflate(g)
2158 h, cff, cfg = f._gcd(g)
2160 return (h.inflate(J), cff.inflate(J), cfg.inflate(J))
2162 def _gcd_zero(f, g):
2163 one, zero = f.ring.one, f.ring.zero
2164 if g.is_nonnegative:
2165 return g, zero, one
2166 else:
2167 return -g, zero, -one
2169 def _gcd_monom(f, g):
2170 ring = f.ring
2171 ground_gcd = ring.domain.gcd
2172 ground_quo = ring.domain.quo
2173 monomial_gcd = ring.monomial_gcd
2174 monomial_ldiv = ring.monomial_ldiv
2175 mf, cf = list(f.iterterms())[0]
2176 _mgcd, _cgcd = mf, cf
2177 for mg, cg in g.iterterms():
2178 _mgcd = monomial_gcd(_mgcd, mg)
2179 _cgcd = ground_gcd(_cgcd, cg)
2180 h = f.new([(_mgcd, _cgcd)])
2181 cff = f.new([(monomial_ldiv(mf, _mgcd), ground_quo(cf, _cgcd))])
2182 cfg = f.new([(monomial_ldiv(mg, _mgcd), ground_quo(cg, _cgcd)) for mg, cg in g.iterterms()])
2183 return h, cff, cfg
2185 def _gcd(f, g):
2186 ring = f.ring
2188 if ring.domain.is_QQ:
2189 return f._gcd_QQ(g)
2190 elif ring.domain.is_ZZ:
2191 return f._gcd_ZZ(g)
2192 else: # TODO: don't use dense representation (port PRS algorithms)
2193 return ring.dmp_inner_gcd(f, g)
2195 def _gcd_ZZ(f, g):
2196 return heugcd(f, g)
2198 def _gcd_QQ(self, g):
2199 f = self
2200 ring = f.ring
2201 new_ring = ring.clone(domain=ring.domain.get_ring())
2203 cf, f = f.clear_denoms()
2204 cg, g = g.clear_denoms()
2206 f = f.set_ring(new_ring)
2207 g = g.set_ring(new_ring)
2209 h, cff, cfg = f._gcd_ZZ(g)
2211 h = h.set_ring(ring)
2212 c, h = h.LC, h.monic()
2214 cff = cff.set_ring(ring).mul_ground(ring.domain.quo(c, cf))
2215 cfg = cfg.set_ring(ring).mul_ground(ring.domain.quo(c, cg))
2217 return h, cff, cfg
2219 def cancel(self, g):
2220 """
2221 Cancel common factors in a rational function ``f/g``.
2223 Examples
2224 ========
2226 >>> from sympy.polys import ring, ZZ
2227 >>> R, x,y = ring("x,y", ZZ)
2229 >>> (2*x**2 - 2).cancel(x**2 - 2*x + 1)
2230 (2*x + 2, x - 1)
2232 """
2233 f = self
2234 ring = f.ring
2236 if not f:
2237 return f, ring.one
2239 domain = ring.domain
2241 if not (domain.is_Field and domain.has_assoc_Ring):
2242 _, p, q = f.cofactors(g)
2243 else:
2244 new_ring = ring.clone(domain=domain.get_ring())
2246 cq, f = f.clear_denoms()
2247 cp, g = g.clear_denoms()
2249 f = f.set_ring(new_ring)
2250 g = g.set_ring(new_ring)
2252 _, p, q = f.cofactors(g)
2253 _, cp, cq = new_ring.domain.cofactors(cp, cq)
2255 p = p.set_ring(ring)
2256 q = q.set_ring(ring)
2258 p = p.mul_ground(cp)
2259 q = q.mul_ground(cq)
2261 # Make canonical with respect to sign or quadrant in the case of ZZ_I
2262 # or QQ_I. This ensures that the LC of the denominator is canonical by
2263 # multiplying top and bottom by a unit of the ring.
2264 u = q.canonical_unit()
2265 if u == domain.one:
2266 p, q = p, q
2267 elif u == -domain.one:
2268 p, q = -p, -q
2269 else:
2270 p = p.mul_ground(u)
2271 q = q.mul_ground(u)
2273 return p, q
2275 def canonical_unit(f):
2276 domain = f.ring.domain
2277 return domain.canonical_unit(f.LC)
2279 def diff(f, x):
2280 """Computes partial derivative in ``x``.
2282 Examples
2283 ========
2285 >>> from sympy.polys.rings import ring
2286 >>> from sympy.polys.domains import ZZ
2288 >>> _, x, y = ring("x,y", ZZ)
2289 >>> p = x + x**2*y**3
2290 >>> p.diff(x)
2291 2*x*y**3 + 1
2293 """
2294 ring = f.ring
2295 i = ring.index(x)
2296 m = ring.monomial_basis(i)
2297 g = ring.zero
2298 for expv, coeff in f.iterterms():
2299 if expv[i]:
2300 e = ring.monomial_ldiv(expv, m)
2301 g[e] = ring.domain_new(coeff*expv[i])
2302 return g
2304 def __call__(f, *values):
2305 if 0 < len(values) <= f.ring.ngens:
2306 return f.evaluate(list(zip(f.ring.gens, values)))
2307 else:
2308 raise ValueError("expected at least 1 and at most %s values, got %s" % (f.ring.ngens, len(values)))
2310 def evaluate(self, x, a=None):
2311 f = self
2313 if isinstance(x, list) and a is None:
2314 (X, a), x = x[0], x[1:]
2315 f = f.evaluate(X, a)
2317 if not x:
2318 return f
2319 else:
2320 x = [ (Y.drop(X), a) for (Y, a) in x ]
2321 return f.evaluate(x)
2323 ring = f.ring
2324 i = ring.index(x)
2325 a = ring.domain.convert(a)
2327 if ring.ngens == 1:
2328 result = ring.domain.zero
2330 for (n,), coeff in f.iterterms():
2331 result += coeff*a**n
2333 return result
2334 else:
2335 poly = ring.drop(x).zero
2337 for monom, coeff in f.iterterms():
2338 n, monom = monom[i], monom[:i] + monom[i+1:]
2339 coeff = coeff*a**n
2341 if monom in poly:
2342 coeff = coeff + poly[monom]
2344 if coeff:
2345 poly[monom] = coeff
2346 else:
2347 del poly[monom]
2348 else:
2349 if coeff:
2350 poly[monom] = coeff
2352 return poly
2354 def subs(self, x, a=None):
2355 f = self
2357 if isinstance(x, list) and a is None:
2358 for X, a in x:
2359 f = f.subs(X, a)
2360 return f
2362 ring = f.ring
2363 i = ring.index(x)
2364 a = ring.domain.convert(a)
2366 if ring.ngens == 1:
2367 result = ring.domain.zero
2369 for (n,), coeff in f.iterterms():
2370 result += coeff*a**n
2372 return ring.ground_new(result)
2373 else:
2374 poly = ring.zero
2376 for monom, coeff in f.iterterms():
2377 n, monom = monom[i], monom[:i] + (0,) + monom[i+1:]
2378 coeff = coeff*a**n
2380 if monom in poly:
2381 coeff = coeff + poly[monom]
2383 if coeff:
2384 poly[monom] = coeff
2385 else:
2386 del poly[monom]
2387 else:
2388 if coeff:
2389 poly[monom] = coeff
2391 return poly
2393 def symmetrize(self):
2394 r"""
2395 Rewrite *self* in terms of elementary symmetric polynomials.
2397 Explanation
2398 ===========
2400 If this :py:class:`~.PolyElement` belongs to a ring of $n$ variables,
2401 we can try to write it as a function of the elementary symmetric
2402 polynomials on $n$ variables. We compute a symmetric part, and a
2403 remainder for any part we were not able to symmetrize.
2405 Examples
2406 ========
2408 >>> from sympy.polys.rings import ring
2409 >>> from sympy.polys.domains import ZZ
2410 >>> R, x, y = ring("x,y", ZZ)
2412 >>> f = x**2 + y**2
2413 >>> f.symmetrize()
2414 (x**2 - 2*y, 0, [(x, x + y), (y, x*y)])
2416 >>> f = x**2 - y**2
2417 >>> f.symmetrize()
2418 (x**2 - 2*y, -2*y**2, [(x, x + y), (y, x*y)])
2420 Returns
2421 =======
2423 Triple ``(p, r, m)``
2424 ``p`` is a :py:class:`~.PolyElement` that represents our attempt
2425 to express *self* as a function of elementary symmetric
2426 polynomials. Each variable in ``p`` stands for one of the
2427 elementary symmetric polynomials. The correspondence is given
2428 by ``m``.
2430 ``r`` is the remainder.
2432 ``m`` is a list of pairs, giving the mapping from variables in
2433 ``p`` to elementary symmetric polynomials.
2435 The triple satisfies the equation ``p.compose(m) + r == self``.
2436 If the remainder ``r`` is zero, *self* is symmetric. If it is
2437 nonzero, we were not able to represent *self* as symmetric.
2439 See Also
2440 ========
2442 sympy.polys.polyfuncs.symmetrize
2444 References
2445 ==========
2447 .. [1] Lauer, E. Algorithms for symmetrical polynomials, Proc. 1976
2448 ACM Symp. on Symbolic and Algebraic Computing, NY 242-247.
2449 https://dl.acm.org/doi/pdf/10.1145/800205.806342
2451 """
2452 f = self.copy()
2453 ring = f.ring
2454 n = ring.ngens
2456 if not n:
2457 return f, ring.zero, []
2459 polys = [ring.symmetric_poly(i+1) for i in range(n)]
2461 poly_powers = {}
2462 def get_poly_power(i, n):
2463 if (i, n) not in poly_powers:
2464 poly_powers[(i, n)] = polys[i]**n
2465 return poly_powers[(i, n)]
2467 indices = list(range(n - 1))
2468 weights = list(range(n, 0, -1))
2470 symmetric = ring.zero
2472 while f:
2473 _height, _monom, _coeff = -1, None, None
2475 for i, (monom, coeff) in enumerate(f.terms()):
2476 if all(monom[i] >= monom[i + 1] for i in indices):
2477 height = max([n*m for n, m in zip(weights, monom)])
2479 if height > _height:
2480 _height, _monom, _coeff = height, monom, coeff
2482 if _height != -1:
2483 monom, coeff = _monom, _coeff
2484 else:
2485 break
2487 exponents = []
2488 for m1, m2 in zip(monom, monom[1:] + (0,)):
2489 exponents.append(m1 - m2)
2491 symmetric += ring.term_new(tuple(exponents), coeff)
2493 product = coeff
2494 for i, n in enumerate(exponents):
2495 product *= get_poly_power(i, n)
2496 f -= product
2498 mapping = list(zip(ring.gens, polys))
2500 return symmetric, f, mapping
2502 def compose(f, x, a=None):
2503 ring = f.ring
2504 poly = ring.zero
2505 gens_map = dict(zip(ring.gens, range(ring.ngens)))
2507 if a is not None:
2508 replacements = [(x, a)]
2509 else:
2510 if isinstance(x, list):
2511 replacements = list(x)
2512 elif isinstance(x, dict):
2513 replacements = sorted(x.items(), key=lambda k: gens_map[k[0]])
2514 else:
2515 raise ValueError("expected a generator, value pair a sequence of such pairs")
2517 for k, (x, g) in enumerate(replacements):
2518 replacements[k] = (gens_map[x], ring.ring_new(g))
2520 for monom, coeff in f.iterterms():
2521 monom = list(monom)
2522 subpoly = ring.one
2524 for i, g in replacements:
2525 n, monom[i] = monom[i], 0
2526 if n:
2527 subpoly *= g**n
2529 subpoly = subpoly.mul_term((tuple(monom), coeff))
2530 poly += subpoly
2532 return poly
2534 # TODO: following methods should point to polynomial
2535 # representation independent algorithm implementations.
2537 def pdiv(f, g):
2538 return f.ring.dmp_pdiv(f, g)
2540 def prem(f, g):
2541 return f.ring.dmp_prem(f, g)
2543 def pquo(f, g):
2544 return f.ring.dmp_quo(f, g)
2546 def pexquo(f, g):
2547 return f.ring.dmp_exquo(f, g)
2549 def half_gcdex(f, g):
2550 return f.ring.dmp_half_gcdex(f, g)
2552 def gcdex(f, g):
2553 return f.ring.dmp_gcdex(f, g)
2555 def subresultants(f, g):
2556 return f.ring.dmp_subresultants(f, g)
2558 def resultant(f, g):
2559 return f.ring.dmp_resultant(f, g)
2561 def discriminant(f):
2562 return f.ring.dmp_discriminant(f)
2564 def decompose(f):
2565 if f.ring.is_univariate:
2566 return f.ring.dup_decompose(f)
2567 else:
2568 raise MultivariatePolynomialError("polynomial decomposition")
2570 def shift(f, a):
2571 if f.ring.is_univariate:
2572 return f.ring.dup_shift(f, a)
2573 else:
2574 raise MultivariatePolynomialError("polynomial shift")
2576 def sturm(f):
2577 if f.ring.is_univariate:
2578 return f.ring.dup_sturm(f)
2579 else:
2580 raise MultivariatePolynomialError("sturm sequence")
2582 def gff_list(f):
2583 return f.ring.dmp_gff_list(f)
2585 def sqf_norm(f):
2586 return f.ring.dmp_sqf_norm(f)
2588 def sqf_part(f):
2589 return f.ring.dmp_sqf_part(f)
2591 def sqf_list(f, all=False):
2592 return f.ring.dmp_sqf_list(f, all=all)
2594 def factor_list(f):
2595 return f.ring.dmp_factor_list(f)