123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595 |
- """Sparse polynomial rings. """
- from __future__ import annotations
- from typing import Any
- from operator import add, mul, lt, le, gt, ge
- from functools import reduce
- from types import GeneratorType
- from sympy.core.expr import Expr
- from sympy.core.numbers import igcd, oo
- from sympy.core.symbol import Symbol, symbols as _symbols
- from sympy.core.sympify import CantSympify, sympify
- from sympy.ntheory.multinomial import multinomial_coefficients
- from sympy.polys.compatibility import IPolys
- from sympy.polys.constructor import construct_domain
- from sympy.polys.densebasic import dmp_to_dict, dmp_from_dict
- from sympy.polys.domains.domainelement import DomainElement
- from sympy.polys.domains.polynomialring import PolynomialRing
- from sympy.polys.heuristicgcd import heugcd
- from sympy.polys.monomials import MonomialOps
- from sympy.polys.orderings import lex
- from sympy.polys.polyerrors import (
- CoercionFailed, GeneratorsError,
- ExactQuotientFailed, MultivariatePolynomialError)
- from sympy.polys.polyoptions import (Domain as DomainOpt,
- Order as OrderOpt, build_options)
- from sympy.polys.polyutils import (expr_from_dict, _dict_reorder,
- _parallel_dict_from_expr)
- from sympy.printing.defaults import DefaultPrinting
- from sympy.utilities import public, subsets
- from sympy.utilities.iterables import is_sequence
- 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:`~.MonomialOrder` 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'>
- """
- _ring = PolyRing(symbols, domain, order)
- return (_ring,) + _ring.gens
- @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:`~.MonomialOrder` 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:`~.MonomialOrder` 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 # noqa:
- 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 import sring, symbols
- >>> 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:
- coeffs = sum([ list(rep.values()) for rep in reps ], [])
- opt.domain, coeffs_dom = construct_domain(coeffs, opt=opt)
- coeff_map = dict(zip(coeffs, coeffs_dom))
- reps = [{m: coeff_map[c] for m, c in rep.items()} for rep in reps]
- _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):
- if isinstance(symbols, str):
- return _symbols(symbols, seq=True) if symbols else ()
- elif isinstance(symbols, Expr):
- return (symbols,)
- elif is_sequence(symbols):
- if all(isinstance(s, str) for s in symbols):
- return _symbols(symbols)
- elif all(isinstance(s, Expr) for s in symbols):
- return symbols
- raise GeneratorsError("expected a string, Symbol or expression or a non-empty sequence of strings, Symbols or expressions")
- _ring_cache: dict[Any, Any] = {}
- class PolyRing(DefaultPrinting, IPolys):
- """Multivariate distributed polynomial ring. """
- def __new__(cls, symbols, domain, order=lex):
- symbols = tuple(_parse_symbols(symbols))
- ngens = len(symbols)
- domain = DomainOpt.preprocess(domain)
- order = OrderOpt.preprocess(order)
- _hash_tuple = (cls.__name__, symbols, ngens, domain, order)
- obj = _ring_cache.get(_hash_tuple)
- if obj is None:
- if domain.is_Composite and set(symbols) & set(domain.symbols):
- raise GeneratorsError("polynomial ring and it's ground domain share generators")
- obj = object.__new__(cls)
- obj._hash_tuple = _hash_tuple
- obj._hash = hash(_hash_tuple)
- obj.dtype = type("PolyElement", (PolyElement,), {"ring": obj})
- obj.symbols = symbols
- obj.ngens = ngens
- obj.domain = domain
- obj.order = order
- obj.zero_monom = (0,)*ngens
- obj.gens = obj._gens()
- obj._gens_set = set(obj.gens)
- obj._one = [(obj.zero_monom, domain.one)]
- if ngens:
- # These expect monomials in at least one variable
- codegen = MonomialOps(ngens)
- obj.monomial_mul = codegen.mul()
- obj.monomial_pow = codegen.pow()
- obj.monomial_mulpow = codegen.mulpow()
- obj.monomial_ldiv = codegen.ldiv()
- obj.monomial_div = codegen.div()
- obj.monomial_lcm = codegen.lcm()
- obj.monomial_gcd = codegen.gcd()
- else:
- monunit = lambda a, b: ()
- obj.monomial_mul = monunit
- obj.monomial_pow = monunit
- obj.monomial_mulpow = lambda a, b, c: ()
- obj.monomial_ldiv = monunit
- obj.monomial_div = monunit
- obj.monomial_lcm = monunit
- obj.monomial_gcd = monunit
- if order is lex:
- obj.leading_expv = max
- else:
- obj.leading_expv = lambda f: max(f, key=order)
- for symbol, generator in zip(obj.symbols, obj.gens):
- if isinstance(symbol, Symbol):
- name = symbol.name
- if not hasattr(obj, name):
- setattr(obj, name, generator)
- _ring_cache[_hash_tuple] = obj
- return obj
- def _gens(self):
- """Return a list of polynomial generators. """
- one = self.domain.one
- _gens = []
- for i in range(self.ngens):
- expv = self.monomial_basis(i)
- poly = self.zero
- poly[expv] = one
- _gens.append(poly)
- return tuple(_gens)
- 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):
- return self._hash
- def __eq__(self, other):
- return isinstance(other, PolyRing) and \
- (self.symbols, self.domain, self.ngens, self.order) == \
- (other.symbols, other.domain, other.ngens, other.order)
- def __ne__(self, other):
- return not self == other
- def clone(self, symbols=None, domain=None, order=None):
- return self.__class__(symbols or self.symbols, domain or self.domain, order or self.order)
- def monomial_basis(self, i):
- """Return the ith-basis element. """
- basis = [0]*self.ngens
- basis[i] = 1
- return tuple(basis)
- @property
- def zero(self):
- return self.dtype()
- @property
- def one(self):
- return self.dtype(self._one)
- def domain_new(self, element, orig_domain=None):
- return self.domain.convert(element, orig_domain)
- def ground_new(self, coeff):
- return self.term_new(self.zero_monom, coeff)
- def term_new(self, monom, coeff):
- coeff = self.domain_new(coeff)
- poly = self.zero
- if coeff:
- poly[monom] = coeff
- return poly
- def ring_new(self, element):
- if isinstance(element, PolyElement):
- 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")
- elif isinstance(element, str):
- raise NotImplementedError("parsing")
- elif isinstance(element, dict):
- return self.from_dict(element)
- elif isinstance(element, list):
- try:
- return self.from_terms(element)
- except ValueError:
- return self.from_list(element)
- elif isinstance(element, Expr):
- return self.from_expr(element)
- else:
- return self.ground_new(element)
- __call__ = ring_new
- def from_dict(self, element, orig_domain=None):
- domain_new = self.domain_new
- poly = self.zero
- for monom, coeff in element.items():
- coeff = domain_new(coeff, orig_domain)
- if coeff:
- poly[monom] = coeff
- return poly
- def from_terms(self, element, orig_domain=None):
- return self.from_dict(dict(element), orig_domain)
- 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)))
- else:
- # XXX: Use as_base_exp() to handle Pow(x, n) and also exp(n)
- # XXX: E can be a generator e.g. sring([exp(2)]) -> ZZ[E]
- base, exp = expr.as_base_exp()
- if exp.is_Integer and exp > 1:
- return _rebuild(base)**int(exp)
- else:
- return self.ground_new(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``. """
- if gen is None:
- if self.ngens:
- i = 0
- else:
- i = -1 # indicate impossible choice
- elif isinstance(gen, int):
- 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)
- elif isinstance(gen, self.dtype):
- try:
- i = self.gens.index(gen)
- except ValueError:
- raise ValueError("invalid generator: %s" % gen)
- elif isinstance(gen, str):
- 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)
- return i
- def drop(self, *gens):
- """Remove specified generators from this ring. """
- indices = set(map(self.index, gens))
- symbols = [ s for i, s in enumerate(self.symbols) if i not in indices ]
- if not symbols:
- return self.domain
- else:
- return self.clone(symbols=symbols)
- 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):
- return PolynomialRing(self)
- def to_field(self):
- from sympy.polys.fields import FracField
- return FracField(self.symbols, self.domain, self.order)
- @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))
- def symmetric_poly(self, n):
- """
- Return the elementary symmetric polynomial of degree *n* over
- this ring's generators.
- """
- if n < 0 or n > self.ngens:
- raise ValueError("Cannot generate symmetric polynomial of order %s for %s" % (n, self.gens))
- elif not n:
- return self.one
- else:
- poly = self.zero
- for s in subsets(range(self.ngens), int(n)):
- monom = tuple(int(i in s) for i in range(self.ngens))
- poly += self.term_new(monom, self.domain.one)
- return poly
- class PolyElement(DomainElement, DefaultPrinting, CantSympify, dict):
- """Element of multivariate distributed polynomial ring. """
- def new(self, init):
- return self.__class__(init)
- def parent(self):
- return self.ring.to_domain()
- 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.
- _hash = self._hash
- if _hash is None:
- self._hash = _hash = hash((self.ring, frozenset(self.items())))
- return _hash
- 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
- """
- return self.new(self)
- def set_ring(self, new_ring):
- if self.ring == new_ring:
- return self
- elif self.ring.symbols != new_ring.symbols:
- terms = list(zip(*_dict_reorder(self, self.ring.symbols, new_ring.symbols)))
- return new_ring.from_terms(terms, self.ring.domain)
- else:
- return new_ring.from_dict(self, self.ring.domain)
- def as_expr(self, *symbols):
- if not symbols:
- symbols = self.ring.symbols
- elif len(symbols) != self.ring.ngens:
- raise ValueError(
- "Wrong number of symbols, expected %s got %s" %
- (self.ring.ngens, len(symbols))
- )
- return expr_from_dict(self.as_expr_dict(), *symbols)
- def as_expr_dict(self):
- to_sympy = self.ring.domain.to_sympy
- return {monom: to_sympy(coeff) for monom, coeff in self.iterterms()}
- def clear_denoms(self):
- domain = self.ring.domain
- if not domain.is_Field or not domain.has_assoc_Ring:
- return domain.one, self
- ground_ring = domain.get_ring()
- common = ground_ring.one
- lcm = ground_ring.lcm
- denom = domain.denom
- for coeff in self.values():
- common = lcm(common, denom(coeff))
- poly = self.new([ (k, v*common) for k, v in self.items() ])
- return common, poly
- def strip_zero(self):
- """Eliminate monomials with zero coefficient. """
- for k, v in list(self.items()):
- if not v:
- del self[k]
- 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
- """
- if not p2:
- return not p1
- elif isinstance(p2, PolyElement) and p2.ring == p1.ring:
- return dict.__eq__(p1, p2)
- elif len(p1) > 1:
- return False
- else:
- return p1.get(p1.ring.zero_monom) == p2
- def __ne__(p1, p2):
- return not p1 == 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
- 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("Cannot 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("Cannot 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("Cannot 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 mon not 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):
- return dict(self)
- def str(self, printer, precedence, exp_pattern, mul_symbol):
- if not self:
- return printer._print(self.ring.domain.zero)
- 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():
- negative = ring.domain.is_negative(coeff)
- sign = " - " if negative else " + "
- sexpvs.append(sign)
- if expv == zm:
- scoeff = printer._print(coeff)
- if negative and scoeff.startswith("-"):
- scoeff = scoeff[1:]
- else:
- if negative:
- coeff = -coeff
- if coeff != self.ring.domain.one:
- 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):
- return not self or (len(self) == 1 and self.ring.zero_monom in self)
- @property
- def is_monomial(self):
- return not self or (len(self) == 1 and self.LC == 1)
- @property
- def is_term(self):
- return len(self) <= 1
- @property
- def is_negative(self):
- return self.ring.domain.is_negative(self.LC)
- @property
- def is_positive(self):
- return self.ring.domain.is_positive(self.LC)
- @property
- def is_nonnegative(self):
- return self.ring.domain.is_nonnegative(self.LC)
- @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):
- if not f.ring.ngens:
- return True
- return f.ring.dmp_sqf_p(f)
- @property
- def is_irreducible(f):
- if not f.ring.ngens:
- return True
- 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):
- return self.new([ (monom, -coeff) for monom, coeff in self.iterterms() ])
- 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
- """
- if not p2:
- return p1.copy()
- ring = p1.ring
- if isinstance(p2, ring.dtype):
- p = p1.copy()
- get = p.get
- zero = ring.domain.zero
- for k, v in p2.items():
- v = get(k, zero) + v
- if v:
- p[k] = v
- else:
- del p[k]
- return p
- 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.__radd__(p1)
- else:
- return NotImplemented
- try:
- cp2 = ring.domain_new(p2)
- except CoercionFailed:
- return NotImplemented
- else:
- p = p1.copy()
- if not cp2:
- return p
- zm = ring.zero_monom
- if zm not in p1.keys():
- p[zm] = cp2
- else:
- if p2 == -p[zm]:
- del p[zm]
- else:
- p[zm] += cp2
- return p
- 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
- """
- if not p2:
- return p1.copy()
- ring = p1.ring
- if isinstance(p2, ring.dtype):
- p = p1.copy()
- get = p.get
- zero = ring.domain.zero
- for k, v in p2.items():
- v = get(k, zero) - v
- if v:
- p[k] = v
- else:
- del p[k]
- return p
- 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.__rsub__(p1)
- else:
- return NotImplemented
- try:
- p2 = ring.domain_new(p2)
- except CoercionFailed:
- return NotImplemented
- else:
- p = p1.copy()
- zm = ring.zero_monom
- if zm not in p1.keys():
- p[zm] = -p2
- else:
- if p2 == p[zm]:
- del p[zm]
- else:
- p[zm] -= p2
- return p
- 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
- """
- ring = p1.ring
- try:
- n = ring.domain_new(n)
- except CoercionFailed:
- return NotImplemented
- else:
- p = ring.zero
- for expv in p1:
- p[expv] = -p1[expv]
- p += n
- return p
- 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
- """
- ring = p1.ring
- p = ring.zero
- if not p1 or not p2:
- return p
- elif isinstance(p2, ring.dtype):
- get = p.get
- zero = ring.domain.zero
- monomial_mul = ring.monomial_mul
- p2it = list(p2.items())
- for exp1, v1 in p1.items():
- for exp2, v2 in p2it:
- exp = monomial_mul(exp1, exp2)
- p[exp] = get(exp, zero) + v1*v2
- p.strip_zero()
- return p
- 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.__rmul__(p1)
- else:
- return NotImplemented
- try:
- p2 = ring.domain_new(p2)
- except CoercionFailed:
- return NotImplemented
- else:
- for exp1, v1 in p1.items():
- v = v1*p2
- if v:
- p[exp1] = v
- return p
- 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
- """
- ring = self.ring
- if not n:
- if self:
- return ring.one
- else:
- raise ValueError("0**0")
- elif len(self) == 1:
- monom, coeff = list(self.items())[0]
- p = ring.zero
- if coeff == ring.domain.one:
- p[ring.monomial_pow(monom, n)] = coeff
- else:
- p[ring.monomial_pow(monom, n)] = coeff**n
- return p
- # 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 actual 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 = multinomial_coefficients(len(self), n).items()
- monomial_mulpow = self.ring.monomial_mulpow
- zero_monom = self.ring.zero_monom
- terms = self.items()
- 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
- elif monom in poly:
- 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
- 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
- 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):
- ring = p1.ring
- if not p2:
- raise ZeroDivisionError("polynomial division")
- elif isinstance(p2, ring.dtype):
- if p2.is_monomial:
- return p1*(p2**(-1))
- else:
- return p1.quo(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.__rtruediv__(p1)
- else:
- return NotImplemented
- try:
- p2 = ring.domain_new(p2)
- except CoercionFailed:
- return NotImplemented
- else:
- return p1.quo_ground(p2)
- def __rtruediv__(p1, p2):
- return NotImplemented
- __floordiv__ = __truediv__
- __rfloordiv__ = __rtruediv__
- # TODO: use // (__floordiv__) for exquo()?
- def _term_div(self):
- zm = self.ring.zero_monom
- domain = self.ring.domain
- domain_quo = domain.quo
- monomial_div = self.ring.monomial_div
- if domain.is_Field:
- def term_div(a_lm_a_lc, b_lm_b_lc):
- a_lm, a_lc = a_lm_a_lc
- b_lm, b_lc = b_lm_b_lc
- if b_lm == zm: # apparently this is a very common case
- monom = a_lm
- else:
- monom = monomial_div(a_lm, b_lm)
- if monom is not None:
- return monom, domain_quo(a_lc, b_lc)
- else:
- return None
- else:
- def term_div(a_lm_a_lc, b_lm_b_lc):
- a_lm, a_lc = a_lm_a_lc
- b_lm, b_lc = b_lm_b_lc
- if b_lm == zm: # apparently this is a very common case
- monom = a_lm
- else:
- monom = monomial_div(a_lm, b_lm)
- if not (monom is None or a_lc % b_lc):
- return monom, domain_quo(a_lc, b_lc)
- else:
- return None
- return term_div
- 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
- """
- ring = self.ring
- ret_single = False
- if isinstance(fv, PolyElement):
- ret_single = True
- fv = [fv]
- if not all(fv):
- raise ZeroDivisionError("polynomial division")
- if not self:
- if ret_single:
- return ring.zero, ring.zero
- else:
- return [], ring.zero
- for f in fv:
- if f.ring != ring:
- raise ValueError('self and f must have the same ring')
- s = len(fv)
- qv = [ring.zero for i in range(s)]
- p = self.copy()
- r = ring.zero
- term_div = self._term_div()
- expvs = [fx.leading_expv() for fx in fv]
- while p:
- i = 0
- divoccurred = 0
- while i < s and divoccurred == 0:
- expv = p.leading_expv()
- term = term_div((expv, p[expv]), (expvs[i], fv[i][expvs[i]]))
- if term is not None:
- expv1, c = term
- qv[i] = qv[i]._iadd_monom((expv1, c))
- p = p._iadd_poly_monom(fv[i], (expv1, -c))
- divoccurred = 1
- else:
- i += 1
- if not divoccurred:
- expv = p.leading_expv()
- r = r._iadd_monom((expv, p[expv]))
- del p[expv]
- if expv == ring.zero_monom:
- r += p
- if ret_single:
- if not qv:
- return ring.zero, r
- else:
- return qv[0], r
- else:
- return qv, r
- def rem(self, G):
- f = self
- if isinstance(G, PolyElement):
- G = [G]
- if not all(G):
- raise ZeroDivisionError("polynomial division")
- ring = f.ring
- domain = ring.domain
- zero = domain.zero
- monomial_mul = ring.monomial_mul
- r = ring.zero
- term_div = f._term_div()
- ltf = f.LT
- f = f.copy()
- get = f.get
- while f:
- for g in G:
- tq = term_div(ltf, g.LT)
- if tq is not None:
- m, c = tq
- for mg, cg in g.iterterms():
- m1 = monomial_mul(mg, m)
- c1 = get(m1, zero) - c*cg
- if not c1:
- del f[m1]
- else:
- f[m1] = c1
- ltm = f.leading_expv()
- if ltm is not None:
- ltf = ltm, f[ltm]
- break
- else:
- ltm, ltc = ltf
- if ltm in r:
- r[ltm] += ltc
- else:
- r[ltm] = ltc
- del f[ltm]
- ltm = f.leading_expv()
- if ltm is not None:
- ltf = ltm, f[ltm]
- return r
- def quo(f, G):
- return f.div(G)[0]
- 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
- """
- if self in self.ring._gens_set:
- cpself = self.copy()
- else:
- cpself = self
- expv, coeff = mc
- c = cpself.get(expv)
- if c is None:
- cpself[expv] = coeff
- else:
- c += coeff
- if c:
- cpself[expv] = c
- else:
- del cpself[expv]
- return cpself
- 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 = self
- if p1 in p1.ring._gens_set:
- p1 = p1.copy()
- (m, c) = mc
- get = p1.get
- zero = p1.ring.domain.zero
- monomial_mul = p1.ring.monomial_mul
- for k, v in p2.items():
- ka = monomial_mul(k, m)
- coeff = get(ka, zero) + v*c
- if coeff:
- p1[ka] = coeff
- else:
- del p1[ka]
- return p1
- 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).
- """
- i = f.ring.index(x)
- if not f:
- return -oo
- elif i < 0:
- return 0
- else:
- return max([ monom[i] for monom in f.itermonoms() ])
- 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
- elif i < 0:
- return 0
- 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)
- """
- if self:
- return self.ring.leading_expv(self)
- else:
- return None
- def _get_coeff(self, expv):
- return self.get(expv, self.ring.domain.zero)
- 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 coefficient. """
- return self._get_coeff(self.ring.zero_monom)
- @property
- def LC(self):
- return self._get_coeff(self.leading_expv())
- @property
- def LM(self):
- expv = self.leading_expv()
- if expv is None:
- return self.ring.zero_monom
- else:
- return expv
- 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):
- expv = self.leading_expv()
- if expv is None:
- return (self.ring.zero_monom, self.ring.domain.zero)
- else:
- return (expv, self._get_coeff(expv))
- 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 is not None:
- 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:`~.MonomialOrder` 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:`~.MonomialOrder` 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:`~.MonomialOrder` 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. """
- return iter(self.values())
- def itermonoms(self):
- """Iterator over monomials of a polynomial. """
- return iter(self.keys())
- def iterterms(self):
- """Iterator over terms of a polynomial. """
- return iter(self.items())
- def listcoeffs(self):
- """Unordered list of polynomial coefficients. """
- return list(self.values())
- def listmonoms(self):
- """Unordered list of polynomial monomials. """
- return list(self.keys())
- 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. """
- domain = f.ring.domain
- cont = domain.zero
- gcd = domain.gcd
- for coeff in f.itercoeffs():
- cont = gcd(cont, coeff)
- return cont
- def primitive(f):
- """Returns content and a primitive polynomial. """
- cont = f.content()
- return cont, f.quo_ground(cont)
- def monic(f):
- """Divides all coefficients by the leading coefficient. """
- if not f:
- return f
- else:
- return f.quo_ground(f.LC)
- def mul_ground(f, x):
- if not x:
- return f.ring.zero
- terms = [ (monom, coeff*x) for monom, coeff in f.iterterms() ]
- return f.new(terms)
- def mul_monom(f, monom):
- monomial_mul = f.ring.monomial_mul
- terms = [ (monomial_mul(f_monom, monom), f_coeff) for f_monom, f_coeff in f.items() ]
- return f.new(terms)
- 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):
- domain = f.ring.domain
- if not x:
- raise ZeroDivisionError('polynomial division')
- if not f or x == domain.one:
- return f
- if domain.is_Field:
- quo = domain.quo
- terms = [ (monom, quo(coeff, x)) for monom, coeff in f.iterterms() ]
- else:
- terms = [ (monom, coeff // x) for monom, coeff in f.iterterms() if not (coeff % x) ]
- return f.new(terms)
- 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):
- if f.ring.domain.is_ZZ:
- terms = []
- for monom, coeff in f.iterterms():
- coeff = coeff % p
- if coeff > p // 2:
- coeff = coeff - p
- terms.append((monom, coeff))
- else:
- terms = [ (monom, coeff % p) for monom, coeff in f.iterterms() ]
- poly = f.new(terms)
- poly.strip_zero()
- return poly
- rem_ground = trunc_ground
- def extract_ground(self, g):
- f = self
- fc = f.content()
- gc = g.content()
- gcd = f.ring.domain.gcd(fc, gc)
- f = f.quo_ground(gcd)
- g = g.quo_ground(gcd)
- return gcd, f, g
- def _norm(f, norm_func):
- if not f:
- return f.ring.domain.zero
- else:
- ground_abs = f.ring.domain.abs
- return norm_func([ ground_abs(coeff) for coeff in f.itercoeffs() ])
- def max_norm(f):
- return f._norm(max)
- def l1_norm(f):
- return f._norm(sum)
- def deflate(f, *G):
- ring = f.ring
- polys = [f] + list(G)
- J = [0]*ring.ngens
- for p in polys:
- for monom in p.itermonoms():
- for i, m in enumerate(monom):
- J[i] = igcd(J[i], m)
- for i, b in enumerate(J):
- if not b:
- J[i] = 1
- J = tuple(J)
- if all(b == 1 for b in J):
- return J, polys
- H = []
- for p in polys:
- h = ring.zero
- for I, coeff in p.iterterms():
- N = [ i // j for i, j in zip(I, J) ]
- h[tuple(N)] = coeff
- H.append(h)
- return J, H
- def inflate(f, J):
- poly = f.ring.zero
- for I, coeff in f.iterterms():
- N = [ i*j for i, j in zip(I, J) ]
- poly[tuple(N)] = coeff
- return poly
- def lcm(self, g):
- f = self
- domain = f.ring.domain
- if not domain.is_Field:
- fc, f = f.primitive()
- gc, g = g.primitive()
- c = domain.lcm(fc, gc)
- h = (f*g).quo(f.gcd(g))
- if not domain.is_Field:
- return h.mul_ground(c)
- else:
- return h.monic()
- def gcd(f, g):
- return f.cofactors(g)[0]
- def cofactors(f, g):
- if not f and not g:
- zero = f.ring.zero
- return zero, zero, zero
- elif not f:
- h, cff, cfg = f._gcd_zero(g)
- return h, cff, cfg
- elif not g:
- h, cfg, cff = g._gcd_zero(f)
- return h, cff, cfg
- elif len(f) == 1:
- h, cff, cfg = f._gcd_monom(g)
- return h, cff, cfg
- elif len(g) == 1:
- h, cfg, cff = g._gcd_monom(f)
- return h, cff, cfg
- J, (f, g) = f.deflate(g)
- h, cff, cfg = f._gcd(g)
- return (h.inflate(J), cff.inflate(J), cfg.inflate(J))
- def _gcd_zero(f, g):
- one, zero = f.ring.one, f.ring.zero
- if g.is_nonnegative:
- return g, zero, one
- else:
- return -g, zero, -one
- def _gcd_monom(f, g):
- ring = f.ring
- ground_gcd = ring.domain.gcd
- ground_quo = ring.domain.quo
- monomial_gcd = ring.monomial_gcd
- monomial_ldiv = ring.monomial_ldiv
- mf, cf = list(f.iterterms())[0]
- _mgcd, _cgcd = mf, cf
- for mg, cg in g.iterterms():
- _mgcd = monomial_gcd(_mgcd, mg)
- _cgcd = ground_gcd(_cgcd, cg)
- h = f.new([(_mgcd, _cgcd)])
- cff = f.new([(monomial_ldiv(mf, _mgcd), ground_quo(cf, _cgcd))])
- cfg = f.new([(monomial_ldiv(mg, _mgcd), ground_quo(cg, _cgcd)) for mg, cg in g.iterterms()])
- return h, cff, cfg
- def _gcd(f, g):
- ring = f.ring
- if ring.domain.is_QQ:
- return f._gcd_QQ(g)
- elif ring.domain.is_ZZ:
- return f._gcd_ZZ(g)
- else: # TODO: don't use dense representation (port PRS algorithms)
- return ring.dmp_inner_gcd(f, g)
- def _gcd_ZZ(f, g):
- return heugcd(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)
- """
- f = self
- ring = f.ring
- if not f:
- return f, ring.one
- domain = ring.domain
- if not (domain.is_Field and domain.has_assoc_Ring):
- _, p, q = f.cofactors(g)
- 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 = p.mul_ground(cp)
- q = q.mul_ground(cq)
- # Make canonical with respect to sign or quadrant in the case of ZZ_I
- # or QQ_I. This ensures that the LC of the denominator is canonical by
- # multiplying top and bottom by a unit of the ring.
- u = q.canonical_unit()
- if u == domain.one:
- p, q = p, q
- elif u == -domain.one:
- p, q = -p, -q
- else:
- p = p.mul_ground(u)
- q = q.mul_ground(u)
- return p, q
- def canonical_unit(f):
- domain = f.ring.domain
- return domain.canonical_unit(f.LC)
- 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
- """
- ring = f.ring
- i = ring.index(x)
- m = ring.monomial_basis(i)
- g = ring.zero
- for expv, coeff in f.iterterms():
- if expv[i]:
- e = ring.monomial_ldiv(expv, m)
- g[e] = ring.domain_new(coeff*expv[i])
- return g
- 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):
- f = self
- if isinstance(x, list) and a is 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)
- 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 result
- else:
- poly = ring.drop(x).zero
- for monom, coeff in f.iterterms():
- n, monom = monom[i], monom[:i] + 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 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 symmetrize(self):
- r"""
- Rewrite *self* in terms of elementary symmetric polynomials.
- Explanation
- ===========
- If this :py:class:`~.PolyElement` belongs to a ring of $n$ variables,
- we can try to write it as a function of the elementary symmetric
- polynomials on $n$ variables. We compute a symmetric part, and a
- remainder for any part we were not able to symmetrize.
- Examples
- ========
- >>> from sympy.polys.rings import ring
- >>> from sympy.polys.domains import ZZ
- >>> R, x, y = ring("x,y", ZZ)
- >>> f = x**2 + y**2
- >>> f.symmetrize()
- (x**2 - 2*y, 0, [(x, x + y), (y, x*y)])
- >>> f = x**2 - y**2
- >>> f.symmetrize()
- (x**2 - 2*y, -2*y**2, [(x, x + y), (y, x*y)])
- Returns
- =======
- Triple ``(p, r, m)``
- ``p`` is a :py:class:`~.PolyElement` that represents our attempt
- to express *self* as a function of elementary symmetric
- polynomials. Each variable in ``p`` stands for one of the
- elementary symmetric polynomials. The correspondence is given
- by ``m``.
- ``r`` is the remainder.
- ``m`` is a list of pairs, giving the mapping from variables in
- ``p`` to elementary symmetric polynomials.
- The triple satisfies the equation ``p.compose(m) + r == self``.
- If the remainder ``r`` is zero, *self* is symmetric. If it is
- nonzero, we were not able to represent *self* as symmetric.
- See Also
- ========
- sympy.polys.polyfuncs.symmetrize
- References
- ==========
- .. [1] Lauer, E. Algorithms for symmetrical polynomials, Proc. 1976
- ACM Symp. on Symbolic and Algebraic Computing, NY 242-247.
- https://dl.acm.org/doi/pdf/10.1145/800205.806342
- """
- f = self.copy()
- ring = f.ring
- n = ring.ngens
- if not n:
- return f, ring.zero, []
- polys = [ring.symmetric_poly(i+1) for i in range(n)]
- poly_powers = {}
- def get_poly_power(i, n):
- if (i, n) not in poly_powers:
- poly_powers[(i, n)] = polys[i]**n
- return poly_powers[(i, n)]
- indices = list(range(n - 1))
- weights = list(range(n, 0, -1))
- symmetric = ring.zero
- while f:
- _height, _monom, _coeff = -1, None, None
- for i, (monom, coeff) in enumerate(f.terms()):
- if all(monom[i] >= monom[i + 1] for i in indices):
- height = max([n*m for n, m in zip(weights, monom)])
- if height > _height:
- _height, _monom, _coeff = height, monom, coeff
- if _height != -1:
- monom, coeff = _monom, _coeff
- else:
- break
- exponents = []
- for m1, m2 in zip(monom, monom[1:] + (0,)):
- exponents.append(m1 - m2)
- symmetric += ring.term_new(tuple(exponents), coeff)
- product = coeff
- for i, n in enumerate(exponents):
- product *= get_poly_power(i, n)
- f -= product
- mapping = list(zip(ring.gens, polys))
- return symmetric, f, mapping
- def compose(f, x, a=None):
- ring = f.ring
- poly = ring.zero
- gens_map = dict(zip(ring.gens, 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(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)
|