quotientring.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. """Implementation of :class:`QuotientRing` class."""
  2. from sympy.polys.agca.modules import FreeModuleQuotientRing
  3. from sympy.polys.domains.ring import Ring
  4. from sympy.polys.polyerrors import NotReversible, CoercionFailed
  5. from sympy.utilities import public
  6. # TODO
  7. # - successive quotients (when quotient ideals are implemented)
  8. # - poly rings over quotients?
  9. # - division by non-units in integral domains?
  10. @public
  11. class QuotientRingElement:
  12. """
  13. Class representing elements of (commutative) quotient rings.
  14. Attributes:
  15. - ring - containing ring
  16. - data - element of ring.ring (i.e. base ring) representing self
  17. """
  18. def __init__(self, ring, data):
  19. self.ring = ring
  20. self.data = data
  21. def __str__(self):
  22. from sympy.printing.str import sstr
  23. data = self.ring.ring.to_sympy(self.data)
  24. return sstr(data) + " + " + str(self.ring.base_ideal)
  25. __repr__ = __str__
  26. def __bool__(self):
  27. return not self.ring.is_zero(self)
  28. def __add__(self, om):
  29. if not isinstance(om, self.__class__) or om.ring != self.ring:
  30. try:
  31. om = self.ring.convert(om)
  32. except (NotImplementedError, CoercionFailed):
  33. return NotImplemented
  34. return self.ring(self.data + om.data)
  35. __radd__ = __add__
  36. def __neg__(self):
  37. return self.ring(self.data*self.ring.ring.convert(-1))
  38. def __sub__(self, om):
  39. return self.__add__(-om)
  40. def __rsub__(self, om):
  41. return (-self).__add__(om)
  42. def __mul__(self, o):
  43. if not isinstance(o, self.__class__):
  44. try:
  45. o = self.ring.convert(o)
  46. except (NotImplementedError, CoercionFailed):
  47. return NotImplemented
  48. return self.ring(self.data*o.data)
  49. __rmul__ = __mul__
  50. def __rtruediv__(self, o):
  51. return self.ring.revert(self)*o
  52. def __truediv__(self, o):
  53. if not isinstance(o, self.__class__):
  54. try:
  55. o = self.ring.convert(o)
  56. except (NotImplementedError, CoercionFailed):
  57. return NotImplemented
  58. return self.ring.revert(o)*self
  59. def __pow__(self, oth):
  60. if oth < 0:
  61. return self.ring.revert(self) ** -oth
  62. return self.ring(self.data ** oth)
  63. def __eq__(self, om):
  64. if not isinstance(om, self.__class__) or om.ring != self.ring:
  65. return False
  66. return self.ring.is_zero(self - om)
  67. def __ne__(self, om):
  68. return not self == om
  69. class QuotientRing(Ring):
  70. """
  71. Class representing (commutative) quotient rings.
  72. You should not usually instantiate this by hand, instead use the constructor
  73. from the base ring in the construction.
  74. >>> from sympy.abc import x
  75. >>> from sympy import QQ
  76. >>> I = QQ.old_poly_ring(x).ideal(x**3 + 1)
  77. >>> QQ.old_poly_ring(x).quotient_ring(I)
  78. QQ[x]/<x**3 + 1>
  79. Shorter versions are possible:
  80. >>> QQ.old_poly_ring(x)/I
  81. QQ[x]/<x**3 + 1>
  82. >>> QQ.old_poly_ring(x)/[x**3 + 1]
  83. QQ[x]/<x**3 + 1>
  84. Attributes:
  85. - ring - the base ring
  86. - base_ideal - the ideal used to form the quotient
  87. """
  88. has_assoc_Ring = True
  89. has_assoc_Field = False
  90. dtype = QuotientRingElement
  91. def __init__(self, ring, ideal):
  92. if not ideal.ring == ring:
  93. raise ValueError('Ideal must belong to %s, got %s' % (ring, ideal))
  94. self.ring = ring
  95. self.base_ideal = ideal
  96. self.zero = self(self.ring.zero)
  97. self.one = self(self.ring.one)
  98. def __str__(self):
  99. return str(self.ring) + "/" + str(self.base_ideal)
  100. def __hash__(self):
  101. return hash((self.__class__.__name__, self.dtype, self.ring, self.base_ideal))
  102. def new(self, a):
  103. """Construct an element of ``self`` domain from ``a``. """
  104. if not isinstance(a, self.ring.dtype):
  105. a = self.ring(a)
  106. # TODO optionally disable reduction?
  107. return self.dtype(self, self.base_ideal.reduce_element(a))
  108. def __eq__(self, other):
  109. """Returns ``True`` if two domains are equivalent. """
  110. return isinstance(other, QuotientRing) and \
  111. self.ring == other.ring and self.base_ideal == other.base_ideal
  112. def from_ZZ(K1, a, K0):
  113. """Convert a Python ``int`` object to ``dtype``. """
  114. return K1(K1.ring.convert(a, K0))
  115. from_ZZ_python = from_ZZ
  116. from_QQ_python = from_ZZ_python
  117. from_ZZ_gmpy = from_ZZ_python
  118. from_QQ_gmpy = from_ZZ_python
  119. from_RealField = from_ZZ_python
  120. from_GlobalPolynomialRing = from_ZZ_python
  121. from_FractionField = from_ZZ_python
  122. def from_sympy(self, a):
  123. return self(self.ring.from_sympy(a))
  124. def to_sympy(self, a):
  125. return self.ring.to_sympy(a.data)
  126. def from_QuotientRing(self, a, K0):
  127. if K0 == self:
  128. return a
  129. def poly_ring(self, *gens):
  130. """Returns a polynomial ring, i.e. ``K[X]``. """
  131. raise NotImplementedError('nested domains not allowed')
  132. def frac_field(self, *gens):
  133. """Returns a fraction field, i.e. ``K(X)``. """
  134. raise NotImplementedError('nested domains not allowed')
  135. def revert(self, a):
  136. """
  137. Compute a**(-1), if possible.
  138. """
  139. I = self.ring.ideal(a.data) + self.base_ideal
  140. try:
  141. return self(I.in_terms_of_generators(1)[0])
  142. except ValueError: # 1 not in I
  143. raise NotReversible('%s not a unit in %r' % (a, self))
  144. def is_zero(self, a):
  145. return self.base_ideal.contains(a.data)
  146. def free_module(self, rank):
  147. """
  148. Generate a free module of rank ``rank`` over ``self``.
  149. >>> from sympy.abc import x
  150. >>> from sympy import QQ
  151. >>> (QQ.old_poly_ring(x)/[x**2 + 1]).free_module(2)
  152. (QQ[x]/<x**2 + 1>)**2
  153. """
  154. return FreeModuleQuotientRing(self, rank)