ideals.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  1. """Computations with ideals of polynomial rings."""
  2. from sympy.polys.polyerrors import CoercionFailed
  3. from sympy.polys.polyutils import IntegerPowerable
  4. class Ideal(IntegerPowerable):
  5. """
  6. Abstract base class for ideals.
  7. Do not instantiate - use explicit constructors in the ring class instead:
  8. >>> from sympy import QQ
  9. >>> from sympy.abc import x
  10. >>> QQ.old_poly_ring(x).ideal(x+1)
  11. <x + 1>
  12. Attributes
  13. - ring - the ring this ideal belongs to
  14. Non-implemented methods:
  15. - _contains_elem
  16. - _contains_ideal
  17. - _quotient
  18. - _intersect
  19. - _union
  20. - _product
  21. - is_whole_ring
  22. - is_zero
  23. - is_prime, is_maximal, is_primary, is_radical
  24. - is_principal
  25. - height, depth
  26. - radical
  27. Methods that likely should be overridden in subclasses:
  28. - reduce_element
  29. """
  30. def _contains_elem(self, x):
  31. """Implementation of element containment."""
  32. raise NotImplementedError
  33. def _contains_ideal(self, I):
  34. """Implementation of ideal containment."""
  35. raise NotImplementedError
  36. def _quotient(self, J):
  37. """Implementation of ideal quotient."""
  38. raise NotImplementedError
  39. def _intersect(self, J):
  40. """Implementation of ideal intersection."""
  41. raise NotImplementedError
  42. def is_whole_ring(self):
  43. """Return True if ``self`` is the whole ring."""
  44. raise NotImplementedError
  45. def is_zero(self):
  46. """Return True if ``self`` is the zero ideal."""
  47. raise NotImplementedError
  48. def _equals(self, J):
  49. """Implementation of ideal equality."""
  50. return self._contains_ideal(J) and J._contains_ideal(self)
  51. def is_prime(self):
  52. """Return True if ``self`` is a prime ideal."""
  53. raise NotImplementedError
  54. def is_maximal(self):
  55. """Return True if ``self`` is a maximal ideal."""
  56. raise NotImplementedError
  57. def is_radical(self):
  58. """Return True if ``self`` is a radical ideal."""
  59. raise NotImplementedError
  60. def is_primary(self):
  61. """Return True if ``self`` is a primary ideal."""
  62. raise NotImplementedError
  63. def is_principal(self):
  64. """Return True if ``self`` is a principal ideal."""
  65. raise NotImplementedError
  66. def radical(self):
  67. """Compute the radical of ``self``."""
  68. raise NotImplementedError
  69. def depth(self):
  70. """Compute the depth of ``self``."""
  71. raise NotImplementedError
  72. def height(self):
  73. """Compute the height of ``self``."""
  74. raise NotImplementedError
  75. # TODO more
  76. # non-implemented methods end here
  77. def __init__(self, ring):
  78. self.ring = ring
  79. def _check_ideal(self, J):
  80. """Helper to check ``J`` is an ideal of our ring."""
  81. if not isinstance(J, Ideal) or J.ring != self.ring:
  82. raise ValueError(
  83. 'J must be an ideal of %s, got %s' % (self.ring, J))
  84. def contains(self, elem):
  85. """
  86. Return True if ``elem`` is an element of this ideal.
  87. Examples
  88. ========
  89. >>> from sympy.abc import x
  90. >>> from sympy import QQ
  91. >>> QQ.old_poly_ring(x).ideal(x+1, x-1).contains(3)
  92. True
  93. >>> QQ.old_poly_ring(x).ideal(x**2, x**3).contains(x)
  94. False
  95. """
  96. return self._contains_elem(self.ring.convert(elem))
  97. def subset(self, other):
  98. """
  99. Returns True if ``other`` is is a subset of ``self``.
  100. Here ``other`` may be an ideal.
  101. Examples
  102. ========
  103. >>> from sympy.abc import x
  104. >>> from sympy import QQ
  105. >>> I = QQ.old_poly_ring(x).ideal(x+1)
  106. >>> I.subset([x**2 - 1, x**2 + 2*x + 1])
  107. True
  108. >>> I.subset([x**2 + 1, x + 1])
  109. False
  110. >>> I.subset(QQ.old_poly_ring(x).ideal(x**2 - 1))
  111. True
  112. """
  113. if isinstance(other, Ideal):
  114. return self._contains_ideal(other)
  115. return all(self._contains_elem(x) for x in other)
  116. def quotient(self, J, **opts):
  117. r"""
  118. Compute the ideal quotient of ``self`` by ``J``.
  119. That is, if ``self`` is the ideal `I`, compute the set
  120. `I : J = \{x \in R | xJ \subset I \}`.
  121. Examples
  122. ========
  123. >>> from sympy.abc import x, y
  124. >>> from sympy import QQ
  125. >>> R = QQ.old_poly_ring(x, y)
  126. >>> R.ideal(x*y).quotient(R.ideal(x))
  127. <y>
  128. """
  129. self._check_ideal(J)
  130. return self._quotient(J, **opts)
  131. def intersect(self, J):
  132. """
  133. Compute the intersection of self with ideal J.
  134. Examples
  135. ========
  136. >>> from sympy.abc import x, y
  137. >>> from sympy import QQ
  138. >>> R = QQ.old_poly_ring(x, y)
  139. >>> R.ideal(x).intersect(R.ideal(y))
  140. <x*y>
  141. """
  142. self._check_ideal(J)
  143. return self._intersect(J)
  144. def saturate(self, J):
  145. r"""
  146. Compute the ideal saturation of ``self`` by ``J``.
  147. That is, if ``self`` is the ideal `I`, compute the set
  148. `I : J^\infty = \{x \in R | xJ^n \subset I \text{ for some } n\}`.
  149. """
  150. raise NotImplementedError
  151. # Note this can be implemented using repeated quotient
  152. def union(self, J):
  153. """
  154. Compute the ideal generated by the union of ``self`` and ``J``.
  155. Examples
  156. ========
  157. >>> from sympy.abc import x
  158. >>> from sympy import QQ
  159. >>> QQ.old_poly_ring(x).ideal(x**2 - 1).union(QQ.old_poly_ring(x).ideal((x+1)**2)) == QQ.old_poly_ring(x).ideal(x+1)
  160. True
  161. """
  162. self._check_ideal(J)
  163. return self._union(J)
  164. def product(self, J):
  165. r"""
  166. Compute the ideal product of ``self`` and ``J``.
  167. That is, compute the ideal generated by products `xy`, for `x` an element
  168. of ``self`` and `y \in J`.
  169. Examples
  170. ========
  171. >>> from sympy.abc import x, y
  172. >>> from sympy import QQ
  173. >>> QQ.old_poly_ring(x, y).ideal(x).product(QQ.old_poly_ring(x, y).ideal(y))
  174. <x*y>
  175. """
  176. self._check_ideal(J)
  177. return self._product(J)
  178. def reduce_element(self, x):
  179. """
  180. Reduce the element ``x`` of our ring modulo the ideal ``self``.
  181. Here "reduce" has no specific meaning: it could return a unique normal
  182. form, simplify the expression a bit, or just do nothing.
  183. """
  184. return x
  185. def __add__(self, e):
  186. if not isinstance(e, Ideal):
  187. R = self.ring.quotient_ring(self)
  188. if isinstance(e, R.dtype):
  189. return e
  190. if isinstance(e, R.ring.dtype):
  191. return R(e)
  192. return R.convert(e)
  193. self._check_ideal(e)
  194. return self.union(e)
  195. __radd__ = __add__
  196. def __mul__(self, e):
  197. if not isinstance(e, Ideal):
  198. try:
  199. e = self.ring.ideal(e)
  200. except CoercionFailed:
  201. return NotImplemented
  202. self._check_ideal(e)
  203. return self.product(e)
  204. __rmul__ = __mul__
  205. def _zeroth_power(self):
  206. return self.ring.ideal(1)
  207. def _first_power(self):
  208. # Raising to any power but 1 returns a new instance. So we mult by 1
  209. # here so that the first power is no exception.
  210. return self * 1
  211. def __eq__(self, e):
  212. if not isinstance(e, Ideal) or e.ring != self.ring:
  213. return False
  214. return self._equals(e)
  215. def __ne__(self, e):
  216. return not (self == e)
  217. class ModuleImplementedIdeal(Ideal):
  218. """
  219. Ideal implementation relying on the modules code.
  220. Attributes:
  221. - _module - the underlying module
  222. """
  223. def __init__(self, ring, module):
  224. Ideal.__init__(self, ring)
  225. self._module = module
  226. def _contains_elem(self, x):
  227. return self._module.contains([x])
  228. def _contains_ideal(self, J):
  229. if not isinstance(J, ModuleImplementedIdeal):
  230. raise NotImplementedError
  231. return self._module.is_submodule(J._module)
  232. def _intersect(self, J):
  233. if not isinstance(J, ModuleImplementedIdeal):
  234. raise NotImplementedError
  235. return self.__class__(self.ring, self._module.intersect(J._module))
  236. def _quotient(self, J, **opts):
  237. if not isinstance(J, ModuleImplementedIdeal):
  238. raise NotImplementedError
  239. return self._module.module_quotient(J._module, **opts)
  240. def _union(self, J):
  241. if not isinstance(J, ModuleImplementedIdeal):
  242. raise NotImplementedError
  243. return self.__class__(self.ring, self._module.union(J._module))
  244. @property
  245. def gens(self):
  246. """
  247. Return generators for ``self``.
  248. Examples
  249. ========
  250. >>> from sympy import QQ
  251. >>> from sympy.abc import x, y
  252. >>> list(QQ.old_poly_ring(x, y).ideal(x, y, x**2 + y).gens)
  253. [x, y, x**2 + y]
  254. """
  255. return (x[0] for x in self._module.gens)
  256. def is_zero(self):
  257. """
  258. Return True if ``self`` is the zero ideal.
  259. Examples
  260. ========
  261. >>> from sympy.abc import x
  262. >>> from sympy import QQ
  263. >>> QQ.old_poly_ring(x).ideal(x).is_zero()
  264. False
  265. >>> QQ.old_poly_ring(x).ideal().is_zero()
  266. True
  267. """
  268. return self._module.is_zero()
  269. def is_whole_ring(self):
  270. """
  271. Return True if ``self`` is the whole ring, i.e. one generator is a unit.
  272. Examples
  273. ========
  274. >>> from sympy.abc import x
  275. >>> from sympy import QQ, ilex
  276. >>> QQ.old_poly_ring(x).ideal(x).is_whole_ring()
  277. False
  278. >>> QQ.old_poly_ring(x).ideal(3).is_whole_ring()
  279. True
  280. >>> QQ.old_poly_ring(x, order=ilex).ideal(2 + x).is_whole_ring()
  281. True
  282. """
  283. return self._module.is_full_module()
  284. def __repr__(self):
  285. from sympy.printing.str import sstr
  286. return '<' + ','.join(sstr(x) for [x] in self._module.gens) + '>'
  287. # NOTE this is the only method using the fact that the module is a SubModule
  288. def _product(self, J):
  289. if not isinstance(J, ModuleImplementedIdeal):
  290. raise NotImplementedError
  291. return self.__class__(self.ring, self._module.submodule(
  292. *[[x*y] for [x] in self._module.gens for [y] in J._module.gens]))
  293. def in_terms_of_generators(self, e):
  294. """
  295. Express ``e`` in terms of the generators of ``self``.
  296. Examples
  297. ========
  298. >>> from sympy.abc import x
  299. >>> from sympy import QQ
  300. >>> I = QQ.old_poly_ring(x).ideal(x**2 + 1, x)
  301. >>> I.in_terms_of_generators(1)
  302. [1, -x]
  303. """
  304. return self._module.in_terms_of_generators([e])
  305. def reduce_element(self, x, **options):
  306. return self._module.reduce_element([x], **options)[0]