123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462 |
- """Implementation of :class:`PolynomialRing` class. """
- from sympy.polys.agca.modules import FreeModulePolyRing
- from sympy.polys.domains.characteristiczero import CharacteristicZero
- from sympy.polys.domains.compositedomain import CompositeDomain
- from sympy.polys.domains.old_fractionfield import FractionField
- from sympy.polys.domains.ring import Ring
- from sympy.polys.orderings import monomial_key, build_product_order
- from sympy.polys.polyclasses import DMP, DMF
- from sympy.polys.polyerrors import (GeneratorsNeeded, PolynomialError,
- CoercionFailed, ExactQuotientFailed, NotReversible)
- from sympy.polys.polyutils import dict_from_basic, basic_from_dict, _dict_reorder
- from sympy.utilities import public
- from sympy.utilities.iterables import iterable
- # XXX why does this derive from CharacteristicZero???
- @public
- class PolynomialRingBase(Ring, CharacteristicZero, CompositeDomain):
- """
- Base class for generalized polynomial rings.
- This base class should be used for uniform access to generalized polynomial
- rings. Subclasses only supply information about the element storage etc.
- Do not instantiate.
- """
- has_assoc_Ring = True
- has_assoc_Field = True
- default_order = "grevlex"
- def __init__(self, dom, *gens, **opts):
- if not gens:
- raise GeneratorsNeeded("generators not specified")
- lev = len(gens) - 1
- self.ngens = len(gens)
- self.zero = self.dtype.zero(lev, dom, ring=self)
- self.one = self.dtype.one(lev, dom, ring=self)
- self.domain = self.dom = dom
- self.symbols = self.gens = gens
- # NOTE 'order' may not be set if inject was called through CompositeDomain
- self.order = opts.get('order', monomial_key(self.default_order))
- def new(self, element):
- return self.dtype(element, self.dom, len(self.gens) - 1, ring=self)
- def __str__(self):
- s_order = str(self.order)
- orderstr = (
- " order=" + s_order) if s_order != self.default_order else ""
- return str(self.dom) + '[' + ','.join(map(str, self.gens)) + orderstr + ']'
- def __hash__(self):
- return hash((self.__class__.__name__, self.dtype, self.dom,
- self.gens, self.order))
- def __eq__(self, other):
- """Returns ``True`` if two domains are equivalent. """
- return isinstance(other, PolynomialRingBase) and \
- self.dtype == other.dtype and self.dom == other.dom and \
- self.gens == other.gens and self.order == other.order
- def from_ZZ(K1, a, K0):
- """Convert a Python ``int`` object to ``dtype``. """
- return K1(K1.dom.convert(a, K0))
- def from_ZZ_python(K1, a, K0):
- """Convert a Python ``int`` object to ``dtype``. """
- return K1(K1.dom.convert(a, K0))
- def from_QQ(K1, a, K0):
- """Convert a Python ``Fraction`` object to ``dtype``. """
- return K1(K1.dom.convert(a, K0))
- def from_QQ_python(K1, a, K0):
- """Convert a Python ``Fraction`` object to ``dtype``. """
- return K1(K1.dom.convert(a, K0))
- def from_ZZ_gmpy(K1, a, K0):
- """Convert a GMPY ``mpz`` object to ``dtype``. """
- return K1(K1.dom.convert(a, K0))
- def from_QQ_gmpy(K1, a, K0):
- """Convert a GMPY ``mpq`` object to ``dtype``. """
- return K1(K1.dom.convert(a, K0))
- def from_RealField(K1, a, K0):
- """Convert a mpmath ``mpf`` object to ``dtype``. """
- return K1(K1.dom.convert(a, K0))
- def from_AlgebraicField(K1, a, K0):
- """Convert a ``ANP`` object to ``dtype``. """
- if K1.dom == K0:
- return K1(a)
- def from_PolynomialRing(K1, a, K0):
- """Convert a ``PolyElement`` object to ``dtype``. """
- if K1.gens == K0.symbols:
- if K1.dom == K0.dom:
- return K1(dict(a)) # set the correct ring
- else:
- convert_dom = lambda c: K1.dom.convert_from(c, K0.dom)
- return K1({m: convert_dom(c) for m, c in a.items()})
- else:
- monoms, coeffs = _dict_reorder(a.to_dict(), K0.symbols, K1.gens)
- if K1.dom != K0.dom:
- coeffs = [ K1.dom.convert(c, K0.dom) for c in coeffs ]
- return K1(dict(zip(monoms, coeffs)))
- def from_GlobalPolynomialRing(K1, a, K0):
- """Convert a ``DMP`` object to ``dtype``. """
- if K1.gens == K0.gens:
- if K1.dom == K0.dom:
- return K1(a.rep) # set the correct ring
- else:
- return K1(a.convert(K1.dom).rep)
- else:
- monoms, coeffs = _dict_reorder(a.to_dict(), K0.gens, K1.gens)
- if K1.dom != K0.dom:
- coeffs = [ K1.dom.convert(c, K0.dom) for c in coeffs ]
- return K1(dict(zip(monoms, coeffs)))
- def get_field(self):
- """Returns a field associated with ``self``. """
- return FractionField(self.dom, *self.gens)
- def poly_ring(self, *gens):
- """Returns a polynomial ring, i.e. ``K[X]``. """
- raise NotImplementedError('nested domains not allowed')
- def frac_field(self, *gens):
- """Returns a fraction field, i.e. ``K(X)``. """
- raise NotImplementedError('nested domains not allowed')
- def revert(self, a):
- try:
- return 1/a
- except (ExactQuotientFailed, ZeroDivisionError):
- raise NotReversible('%s is not a unit' % a)
- def gcdex(self, a, b):
- """Extended GCD of ``a`` and ``b``. """
- return a.gcdex(b)
- def gcd(self, a, b):
- """Returns GCD of ``a`` and ``b``. """
- return a.gcd(b)
- def lcm(self, a, b):
- """Returns LCM of ``a`` and ``b``. """
- return a.lcm(b)
- def factorial(self, a):
- """Returns factorial of ``a``. """
- return self.dtype(self.dom.factorial(a))
- def _vector_to_sdm(self, v, order):
- """
- For internal use by the modules class.
- Convert an iterable of elements of this ring into a sparse distributed
- module element.
- """
- raise NotImplementedError
- def _sdm_to_dics(self, s, n):
- """Helper for _sdm_to_vector."""
- from sympy.polys.distributedmodules import sdm_to_dict
- dic = sdm_to_dict(s)
- res = [{} for _ in range(n)]
- for k, v in dic.items():
- res[k[0]][k[1:]] = v
- return res
- def _sdm_to_vector(self, s, n):
- """
- For internal use by the modules class.
- Convert a sparse distributed module into a list of length ``n``.
- Examples
- ========
- >>> from sympy import QQ, ilex
- >>> from sympy.abc import x, y
- >>> R = QQ.old_poly_ring(x, y, order=ilex)
- >>> L = [((1, 1, 1), QQ(1)), ((0, 1, 0), QQ(1)), ((0, 0, 1), QQ(2))]
- >>> R._sdm_to_vector(L, 2)
- [x + 2*y, x*y]
- """
- dics = self._sdm_to_dics(s, n)
- # NOTE this works for global and local rings!
- return [self(x) for x in dics]
- def free_module(self, rank):
- """
- Generate a free module of rank ``rank`` over ``self``.
- Examples
- ========
- >>> from sympy.abc import x
- >>> from sympy import QQ
- >>> QQ.old_poly_ring(x).free_module(2)
- QQ[x]**2
- """
- return FreeModulePolyRing(self, rank)
- def _vector_to_sdm_helper(v, order):
- """Helper method for common code in Global and Local poly rings."""
- from sympy.polys.distributedmodules import sdm_from_dict
- d = {}
- for i, e in enumerate(v):
- for key, value in e.to_dict().items():
- d[(i,) + key] = value
- return sdm_from_dict(d, order)
- @public
- class GlobalPolynomialRing(PolynomialRingBase):
- """A true polynomial ring, with objects DMP. """
- is_PolynomialRing = is_Poly = True
- dtype = DMP
- def from_FractionField(K1, a, K0):
- """
- Convert a ``DMF`` object to ``DMP``.
- Examples
- ========
- >>> from sympy.polys.polyclasses import DMP, DMF
- >>> from sympy.polys.domains import ZZ
- >>> from sympy.abc import x
- >>> f = DMF(([ZZ(1), ZZ(1)], [ZZ(1)]), ZZ)
- >>> K = ZZ.old_frac_field(x)
- >>> F = ZZ.old_poly_ring(x).from_FractionField(f, K)
- >>> F == DMP([ZZ(1), ZZ(1)], ZZ)
- True
- >>> type(F)
- <class 'sympy.polys.polyclasses.DMP'>
- """
- if a.denom().is_one:
- return K1.from_GlobalPolynomialRing(a.numer(), K0)
- def to_sympy(self, a):
- """Convert ``a`` to a SymPy object. """
- return basic_from_dict(a.to_sympy_dict(), *self.gens)
- def from_sympy(self, a):
- """Convert SymPy's expression to ``dtype``. """
- try:
- rep, _ = dict_from_basic(a, gens=self.gens)
- except PolynomialError:
- raise CoercionFailed("Cannot convert %s to type %s" % (a, self))
- for k, v in rep.items():
- rep[k] = self.dom.from_sympy(v)
- return self(rep)
- def is_positive(self, a):
- """Returns True if ``LC(a)`` is positive. """
- return self.dom.is_positive(a.LC())
- def is_negative(self, a):
- """Returns True if ``LC(a)`` is negative. """
- return self.dom.is_negative(a.LC())
- def is_nonpositive(self, a):
- """Returns True if ``LC(a)`` is non-positive. """
- return self.dom.is_nonpositive(a.LC())
- def is_nonnegative(self, a):
- """Returns True if ``LC(a)`` is non-negative. """
- return self.dom.is_nonnegative(a.LC())
- def _vector_to_sdm(self, v, order):
- """
- Examples
- ========
- >>> from sympy import lex, QQ
- >>> from sympy.abc import x, y
- >>> R = QQ.old_poly_ring(x, y)
- >>> f = R.convert(x + 2*y)
- >>> g = R.convert(x * y)
- >>> R._vector_to_sdm([f, g], lex)
- [((1, 1, 1), 1), ((0, 1, 0), 1), ((0, 0, 1), 2)]
- """
- return _vector_to_sdm_helper(v, order)
- class GeneralizedPolynomialRing(PolynomialRingBase):
- """A generalized polynomial ring, with objects DMF. """
- dtype = DMF
- def new(self, a):
- """Construct an element of ``self`` domain from ``a``. """
- res = self.dtype(a, self.dom, len(self.gens) - 1, ring=self)
- # make sure res is actually in our ring
- if res.denom().terms(order=self.order)[0][0] != (0,)*len(self.gens):
- from sympy.printing.str import sstr
- raise CoercionFailed("denominator %s not allowed in %s"
- % (sstr(res), self))
- return res
- def __contains__(self, a):
- try:
- a = self.convert(a)
- except CoercionFailed:
- return False
- return a.denom().terms(order=self.order)[0][0] == (0,)*len(self.gens)
- def from_FractionField(K1, a, K0):
- dmf = K1.get_field().from_FractionField(a, K0)
- return K1((dmf.num, dmf.den))
- def to_sympy(self, a):
- """Convert ``a`` to a SymPy object. """
- return (basic_from_dict(a.numer().to_sympy_dict(), *self.gens) /
- basic_from_dict(a.denom().to_sympy_dict(), *self.gens))
- def from_sympy(self, a):
- """Convert SymPy's expression to ``dtype``. """
- p, q = a.as_numer_denom()
- num, _ = dict_from_basic(p, gens=self.gens)
- den, _ = dict_from_basic(q, gens=self.gens)
- for k, v in num.items():
- num[k] = self.dom.from_sympy(v)
- for k, v in den.items():
- den[k] = self.dom.from_sympy(v)
- return self((num, den)).cancel()
- def _vector_to_sdm(self, v, order):
- """
- Turn an iterable into a sparse distributed module.
- Note that the vector is multiplied by a unit first to make all entries
- polynomials.
- Examples
- ========
- >>> from sympy import ilex, QQ
- >>> from sympy.abc import x, y
- >>> R = QQ.old_poly_ring(x, y, order=ilex)
- >>> f = R.convert((x + 2*y) / (1 + x))
- >>> g = R.convert(x * y)
- >>> R._vector_to_sdm([f, g], ilex)
- [((0, 0, 1), 2), ((0, 1, 0), 1), ((1, 1, 1), 1), ((1,
- 2, 1), 1)]
- """
- # NOTE this is quite inefficient...
- u = self.one.numer()
- for x in v:
- u *= x.denom()
- return _vector_to_sdm_helper([x.numer()*u/x.denom() for x in v], order)
- @public
- def PolynomialRing(dom, *gens, **opts):
- r"""
- Create a generalized multivariate polynomial ring.
- A generalized polynomial ring is defined by a ground field `K`, a set
- of generators (typically `x_1, \ldots, x_n`) and a monomial order `<`.
- The monomial order can be global, local or mixed. In any case it induces
- a total ordering on the monomials, and there exists for every (non-zero)
- polynomial `f \in K[x_1, \ldots, x_n]` a well-defined "leading monomial"
- `LM(f) = LM(f, >)`. One can then define a multiplicative subset
- `S = S_> = \{f \in K[x_1, \ldots, x_n] | LM(f) = 1\}`. The generalized
- polynomial ring corresponding to the monomial order is
- `R = S^{-1}K[x_1, \ldots, x_n]`.
- If `>` is a so-called global order, that is `1` is the smallest monomial,
- then we just have `S = K` and `R = K[x_1, \ldots, x_n]`.
- Examples
- ========
- A few examples may make this clearer.
- >>> from sympy.abc import x, y
- >>> from sympy import QQ
- Our first ring uses global lexicographic order.
- >>> R1 = QQ.old_poly_ring(x, y, order=(("lex", x, y),))
- The second ring uses local lexicographic order. Note that when using a
- single (non-product) order, you can just specify the name and omit the
- variables:
- >>> R2 = QQ.old_poly_ring(x, y, order="ilex")
- The third and fourth rings use a mixed orders:
- >>> o1 = (("ilex", x), ("lex", y))
- >>> o2 = (("lex", x), ("ilex", y))
- >>> R3 = QQ.old_poly_ring(x, y, order=o1)
- >>> R4 = QQ.old_poly_ring(x, y, order=o2)
- We will investigate what elements of `K(x, y)` are contained in the various
- rings.
- >>> L = [x, 1/x, y/(1 + x), 1/(1 + y), 1/(1 + x*y)]
- >>> test = lambda R: [f in R for f in L]
- The first ring is just `K[x, y]`:
- >>> test(R1)
- [True, False, False, False, False]
- The second ring is R1 localised at the maximal ideal (x, y):
- >>> test(R2)
- [True, False, True, True, True]
- The third ring is R1 localised at the prime ideal (x):
- >>> test(R3)
- [True, False, True, False, True]
- Finally the fourth ring is R1 localised at `S = K[x, y] \setminus yK[y]`:
- >>> test(R4)
- [True, False, False, True, False]
- """
- order = opts.get("order", GeneralizedPolynomialRing.default_order)
- if iterable(order):
- order = build_product_order(order, gens)
- order = monomial_key(order)
- opts['order'] = order
- if order.is_global:
- return GlobalPolynomialRing(dom, *gens, **opts)
- else:
- return GeneralizedPolynomialRing(dom, *gens, **opts)
|