quotientring.py 5.7 KB

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