old_polynomialring.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. """Implementation of :class:`PolynomialRing` class. """
  2. from sympy.polys.agca.modules import FreeModulePolyRing
  3. from sympy.polys.domains.characteristiczero import CharacteristicZero
  4. from sympy.polys.domains.compositedomain import CompositeDomain
  5. from sympy.polys.domains.old_fractionfield import FractionField
  6. from sympy.polys.domains.ring import Ring
  7. from sympy.polys.orderings import monomial_key, build_product_order
  8. from sympy.polys.polyclasses import DMP, DMF
  9. from sympy.polys.polyerrors import (GeneratorsNeeded, PolynomialError,
  10. CoercionFailed, ExactQuotientFailed, NotReversible)
  11. from sympy.polys.polyutils import dict_from_basic, basic_from_dict, _dict_reorder
  12. from sympy.utilities import public
  13. from sympy.utilities.iterables import iterable
  14. # XXX why does this derive from CharacteristicZero???
  15. @public
  16. class PolynomialRingBase(Ring, CharacteristicZero, CompositeDomain):
  17. """
  18. Base class for generalized polynomial rings.
  19. This base class should be used for uniform access to generalized polynomial
  20. rings. Subclasses only supply information about the element storage etc.
  21. Do not instantiate.
  22. """
  23. has_assoc_Ring = True
  24. has_assoc_Field = True
  25. default_order = "grevlex"
  26. def __init__(self, dom, *gens, **opts):
  27. if not gens:
  28. raise GeneratorsNeeded("generators not specified")
  29. lev = len(gens) - 1
  30. self.ngens = len(gens)
  31. self.zero = self.dtype.zero(lev, dom, ring=self)
  32. self.one = self.dtype.one(lev, dom, ring=self)
  33. self.domain = self.dom = dom
  34. self.symbols = self.gens = gens
  35. # NOTE 'order' may not be set if inject was called through CompositeDomain
  36. self.order = opts.get('order', monomial_key(self.default_order))
  37. def new(self, element):
  38. return self.dtype(element, self.dom, len(self.gens) - 1, ring=self)
  39. def __str__(self):
  40. s_order = str(self.order)
  41. orderstr = (
  42. " order=" + s_order) if s_order != self.default_order else ""
  43. return str(self.dom) + '[' + ','.join(map(str, self.gens)) + orderstr + ']'
  44. def __hash__(self):
  45. return hash((self.__class__.__name__, self.dtype, self.dom,
  46. self.gens, self.order))
  47. def __eq__(self, other):
  48. """Returns ``True`` if two domains are equivalent. """
  49. return isinstance(other, PolynomialRingBase) and \
  50. self.dtype == other.dtype and self.dom == other.dom and \
  51. self.gens == other.gens and self.order == other.order
  52. def from_ZZ(K1, a, K0):
  53. """Convert a Python ``int`` object to ``dtype``. """
  54. return K1(K1.dom.convert(a, K0))
  55. def from_ZZ_python(K1, a, K0):
  56. """Convert a Python ``int`` object to ``dtype``. """
  57. return K1(K1.dom.convert(a, K0))
  58. def from_QQ(K1, a, K0):
  59. """Convert a Python ``Fraction`` object to ``dtype``. """
  60. return K1(K1.dom.convert(a, K0))
  61. def from_QQ_python(K1, a, K0):
  62. """Convert a Python ``Fraction`` object to ``dtype``. """
  63. return K1(K1.dom.convert(a, K0))
  64. def from_ZZ_gmpy(K1, a, K0):
  65. """Convert a GMPY ``mpz`` object to ``dtype``. """
  66. return K1(K1.dom.convert(a, K0))
  67. def from_QQ_gmpy(K1, a, K0):
  68. """Convert a GMPY ``mpq`` object to ``dtype``. """
  69. return K1(K1.dom.convert(a, K0))
  70. def from_RealField(K1, a, K0):
  71. """Convert a mpmath ``mpf`` object to ``dtype``. """
  72. return K1(K1.dom.convert(a, K0))
  73. def from_AlgebraicField(K1, a, K0):
  74. """Convert a ``ANP`` object to ``dtype``. """
  75. if K1.dom == K0:
  76. return K1(a)
  77. def from_PolynomialRing(K1, a, K0):
  78. """Convert a ``PolyElement`` object to ``dtype``. """
  79. if K1.gens == K0.symbols:
  80. if K1.dom == K0.dom:
  81. return K1(dict(a)) # set the correct ring
  82. else:
  83. convert_dom = lambda c: K1.dom.convert_from(c, K0.dom)
  84. return K1({m: convert_dom(c) for m, c in a.items()})
  85. else:
  86. monoms, coeffs = _dict_reorder(a.to_dict(), K0.symbols, K1.gens)
  87. if K1.dom != K0.dom:
  88. coeffs = [ K1.dom.convert(c, K0.dom) for c in coeffs ]
  89. return K1(dict(zip(monoms, coeffs)))
  90. def from_GlobalPolynomialRing(K1, a, K0):
  91. """Convert a ``DMP`` object to ``dtype``. """
  92. if K1.gens == K0.gens:
  93. if K1.dom == K0.dom:
  94. return K1(a.rep) # set the correct ring
  95. else:
  96. return K1(a.convert(K1.dom).rep)
  97. else:
  98. monoms, coeffs = _dict_reorder(a.to_dict(), K0.gens, K1.gens)
  99. if K1.dom != K0.dom:
  100. coeffs = [ K1.dom.convert(c, K0.dom) for c in coeffs ]
  101. return K1(dict(zip(monoms, coeffs)))
  102. def get_field(self):
  103. """Returns a field associated with ``self``. """
  104. return FractionField(self.dom, *self.gens)
  105. def poly_ring(self, *gens):
  106. """Returns a polynomial ring, i.e. ``K[X]``. """
  107. raise NotImplementedError('nested domains not allowed')
  108. def frac_field(self, *gens):
  109. """Returns a fraction field, i.e. ``K(X)``. """
  110. raise NotImplementedError('nested domains not allowed')
  111. def revert(self, a):
  112. try:
  113. return 1/a
  114. except (ExactQuotientFailed, ZeroDivisionError):
  115. raise NotReversible('%s is not a unit' % a)
  116. def gcdex(self, a, b):
  117. """Extended GCD of ``a`` and ``b``. """
  118. return a.gcdex(b)
  119. def gcd(self, a, b):
  120. """Returns GCD of ``a`` and ``b``. """
  121. return a.gcd(b)
  122. def lcm(self, a, b):
  123. """Returns LCM of ``a`` and ``b``. """
  124. return a.lcm(b)
  125. def factorial(self, a):
  126. """Returns factorial of ``a``. """
  127. return self.dtype(self.dom.factorial(a))
  128. def _vector_to_sdm(self, v, order):
  129. """
  130. For internal use by the modules class.
  131. Convert an iterable of elements of this ring into a sparse distributed
  132. module element.
  133. """
  134. raise NotImplementedError
  135. def _sdm_to_dics(self, s, n):
  136. """Helper for _sdm_to_vector."""
  137. from sympy.polys.distributedmodules import sdm_to_dict
  138. dic = sdm_to_dict(s)
  139. res = [{} for _ in range(n)]
  140. for k, v in dic.items():
  141. res[k[0]][k[1:]] = v
  142. return res
  143. def _sdm_to_vector(self, s, n):
  144. """
  145. For internal use by the modules class.
  146. Convert a sparse distributed module into a list of length ``n``.
  147. Examples
  148. ========
  149. >>> from sympy import QQ, ilex
  150. >>> from sympy.abc import x, y
  151. >>> R = QQ.old_poly_ring(x, y, order=ilex)
  152. >>> L = [((1, 1, 1), QQ(1)), ((0, 1, 0), QQ(1)), ((0, 0, 1), QQ(2))]
  153. >>> R._sdm_to_vector(L, 2)
  154. [x + 2*y, x*y]
  155. """
  156. dics = self._sdm_to_dics(s, n)
  157. # NOTE this works for global and local rings!
  158. return [self(x) for x in dics]
  159. def free_module(self, rank):
  160. """
  161. Generate a free module of rank ``rank`` over ``self``.
  162. Examples
  163. ========
  164. >>> from sympy.abc import x
  165. >>> from sympy import QQ
  166. >>> QQ.old_poly_ring(x).free_module(2)
  167. QQ[x]**2
  168. """
  169. return FreeModulePolyRing(self, rank)
  170. def _vector_to_sdm_helper(v, order):
  171. """Helper method for common code in Global and Local poly rings."""
  172. from sympy.polys.distributedmodules import sdm_from_dict
  173. d = {}
  174. for i, e in enumerate(v):
  175. for key, value in e.to_dict().items():
  176. d[(i,) + key] = value
  177. return sdm_from_dict(d, order)
  178. @public
  179. class GlobalPolynomialRing(PolynomialRingBase):
  180. """A true polynomial ring, with objects DMP. """
  181. is_PolynomialRing = is_Poly = True
  182. dtype = DMP
  183. def from_FractionField(K1, a, K0):
  184. """
  185. Convert a ``DMF`` object to ``DMP``.
  186. Examples
  187. ========
  188. >>> from sympy.polys.polyclasses import DMP, DMF
  189. >>> from sympy.polys.domains import ZZ
  190. >>> from sympy.abc import x
  191. >>> f = DMF(([ZZ(1), ZZ(1)], [ZZ(1)]), ZZ)
  192. >>> K = ZZ.old_frac_field(x)
  193. >>> F = ZZ.old_poly_ring(x).from_FractionField(f, K)
  194. >>> F == DMP([ZZ(1), ZZ(1)], ZZ)
  195. True
  196. >>> type(F)
  197. <class 'sympy.polys.polyclasses.DMP'>
  198. """
  199. if a.denom().is_one:
  200. return K1.from_GlobalPolynomialRing(a.numer(), K0)
  201. def to_sympy(self, a):
  202. """Convert ``a`` to a SymPy object. """
  203. return basic_from_dict(a.to_sympy_dict(), *self.gens)
  204. def from_sympy(self, a):
  205. """Convert SymPy's expression to ``dtype``. """
  206. try:
  207. rep, _ = dict_from_basic(a, gens=self.gens)
  208. except PolynomialError:
  209. raise CoercionFailed("Cannot convert %s to type %s" % (a, self))
  210. for k, v in rep.items():
  211. rep[k] = self.dom.from_sympy(v)
  212. return self(rep)
  213. def is_positive(self, a):
  214. """Returns True if ``LC(a)`` is positive. """
  215. return self.dom.is_positive(a.LC())
  216. def is_negative(self, a):
  217. """Returns True if ``LC(a)`` is negative. """
  218. return self.dom.is_negative(a.LC())
  219. def is_nonpositive(self, a):
  220. """Returns True if ``LC(a)`` is non-positive. """
  221. return self.dom.is_nonpositive(a.LC())
  222. def is_nonnegative(self, a):
  223. """Returns True if ``LC(a)`` is non-negative. """
  224. return self.dom.is_nonnegative(a.LC())
  225. def _vector_to_sdm(self, v, order):
  226. """
  227. Examples
  228. ========
  229. >>> from sympy import lex, QQ
  230. >>> from sympy.abc import x, y
  231. >>> R = QQ.old_poly_ring(x, y)
  232. >>> f = R.convert(x + 2*y)
  233. >>> g = R.convert(x * y)
  234. >>> R._vector_to_sdm([f, g], lex)
  235. [((1, 1, 1), 1), ((0, 1, 0), 1), ((0, 0, 1), 2)]
  236. """
  237. return _vector_to_sdm_helper(v, order)
  238. class GeneralizedPolynomialRing(PolynomialRingBase):
  239. """A generalized polynomial ring, with objects DMF. """
  240. dtype = DMF
  241. def new(self, a):
  242. """Construct an element of ``self`` domain from ``a``. """
  243. res = self.dtype(a, self.dom, len(self.gens) - 1, ring=self)
  244. # make sure res is actually in our ring
  245. if res.denom().terms(order=self.order)[0][0] != (0,)*len(self.gens):
  246. from sympy.printing.str import sstr
  247. raise CoercionFailed("denominator %s not allowed in %s"
  248. % (sstr(res), self))
  249. return res
  250. def __contains__(self, a):
  251. try:
  252. a = self.convert(a)
  253. except CoercionFailed:
  254. return False
  255. return a.denom().terms(order=self.order)[0][0] == (0,)*len(self.gens)
  256. def from_FractionField(K1, a, K0):
  257. dmf = K1.get_field().from_FractionField(a, K0)
  258. return K1((dmf.num, dmf.den))
  259. def to_sympy(self, a):
  260. """Convert ``a`` to a SymPy object. """
  261. return (basic_from_dict(a.numer().to_sympy_dict(), *self.gens) /
  262. basic_from_dict(a.denom().to_sympy_dict(), *self.gens))
  263. def from_sympy(self, a):
  264. """Convert SymPy's expression to ``dtype``. """
  265. p, q = a.as_numer_denom()
  266. num, _ = dict_from_basic(p, gens=self.gens)
  267. den, _ = dict_from_basic(q, gens=self.gens)
  268. for k, v in num.items():
  269. num[k] = self.dom.from_sympy(v)
  270. for k, v in den.items():
  271. den[k] = self.dom.from_sympy(v)
  272. return self((num, den)).cancel()
  273. def _vector_to_sdm(self, v, order):
  274. """
  275. Turn an iterable into a sparse distributed module.
  276. Note that the vector is multiplied by a unit first to make all entries
  277. polynomials.
  278. Examples
  279. ========
  280. >>> from sympy import ilex, QQ
  281. >>> from sympy.abc import x, y
  282. >>> R = QQ.old_poly_ring(x, y, order=ilex)
  283. >>> f = R.convert((x + 2*y) / (1 + x))
  284. >>> g = R.convert(x * y)
  285. >>> R._vector_to_sdm([f, g], ilex)
  286. [((0, 0, 1), 2), ((0, 1, 0), 1), ((1, 1, 1), 1), ((1,
  287. 2, 1), 1)]
  288. """
  289. # NOTE this is quite inefficient...
  290. u = self.one.numer()
  291. for x in v:
  292. u *= x.denom()
  293. return _vector_to_sdm_helper([x.numer()*u/x.denom() for x in v], order)
  294. @public
  295. def PolynomialRing(dom, *gens, **opts):
  296. r"""
  297. Create a generalized multivariate polynomial ring.
  298. A generalized polynomial ring is defined by a ground field `K`, a set
  299. of generators (typically `x_1, \ldots, x_n`) and a monomial order `<`.
  300. The monomial order can be global, local or mixed. In any case it induces
  301. a total ordering on the monomials, and there exists for every (non-zero)
  302. polynomial `f \in K[x_1, \ldots, x_n]` a well-defined "leading monomial"
  303. `LM(f) = LM(f, >)`. One can then define a multiplicative subset
  304. `S = S_> = \{f \in K[x_1, \ldots, x_n] | LM(f) = 1\}`. The generalized
  305. polynomial ring corresponding to the monomial order is
  306. `R = S^{-1}K[x_1, \ldots, x_n]`.
  307. If `>` is a so-called global order, that is `1` is the smallest monomial,
  308. then we just have `S = K` and `R = K[x_1, \ldots, x_n]`.
  309. Examples
  310. ========
  311. A few examples may make this clearer.
  312. >>> from sympy.abc import x, y
  313. >>> from sympy import QQ
  314. Our first ring uses global lexicographic order.
  315. >>> R1 = QQ.old_poly_ring(x, y, order=(("lex", x, y),))
  316. The second ring uses local lexicographic order. Note that when using a
  317. single (non-product) order, you can just specify the name and omit the
  318. variables:
  319. >>> R2 = QQ.old_poly_ring(x, y, order="ilex")
  320. The third and fourth rings use a mixed orders:
  321. >>> o1 = (("ilex", x), ("lex", y))
  322. >>> o2 = (("lex", x), ("ilex", y))
  323. >>> R3 = QQ.old_poly_ring(x, y, order=o1)
  324. >>> R4 = QQ.old_poly_ring(x, y, order=o2)
  325. We will investigate what elements of `K(x, y)` are contained in the various
  326. rings.
  327. >>> L = [x, 1/x, y/(1 + x), 1/(1 + y), 1/(1 + x*y)]
  328. >>> test = lambda R: [f in R for f in L]
  329. The first ring is just `K[x, y]`:
  330. >>> test(R1)
  331. [True, False, False, False, False]
  332. The second ring is R1 localised at the maximal ideal (x, y):
  333. >>> test(R2)
  334. [True, False, True, True, True]
  335. The third ring is R1 localised at the prime ideal (x):
  336. >>> test(R3)
  337. [True, False, True, False, True]
  338. Finally the fourth ring is R1 localised at `S = K[x, y] \setminus yK[y]`:
  339. >>> test(R4)
  340. [True, False, False, True, False]
  341. """
  342. order = opts.get("order", GeneralizedPolynomialRing.default_order)
  343. if iterable(order):
  344. order = build_product_order(order, gens)
  345. order = monomial_key(order)
  346. opts['order'] = order
  347. if order.is_global:
  348. return GlobalPolynomialRing(dom, *gens, **opts)
  349. else:
  350. return GeneralizedPolynomialRing(dom, *gens, **opts)