old_polynomialring.py 16 KB

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