123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201 |
- """Implementation of :class:`QuotientRing` class."""
- from sympy.polys.agca.modules import FreeModuleQuotientRing
- from sympy.polys.domains.ring import Ring
- from sympy.polys.polyerrors import NotReversible, CoercionFailed
- from sympy.utilities import public
- # TODO
- # - successive quotients (when quotient ideals are implemented)
- # - poly rings over quotients?
- # - division by non-units in integral domains?
- @public
- class QuotientRingElement:
- """
- Class representing elements of (commutative) quotient rings.
- Attributes:
- - ring - containing ring
- - data - element of ring.ring (i.e. base ring) representing self
- """
- def __init__(self, ring, data):
- self.ring = ring
- self.data = data
- def __str__(self):
- from sympy.printing.str import sstr
- return sstr(self.data) + " + " + str(self.ring.base_ideal)
- __repr__ = __str__
- def __bool__(self):
- return not self.ring.is_zero(self)
- def __add__(self, om):
- if not isinstance(om, self.__class__) or om.ring != self.ring:
- try:
- om = self.ring.convert(om)
- except (NotImplementedError, CoercionFailed):
- return NotImplemented
- return self.ring(self.data + om.data)
- __radd__ = __add__
- def __neg__(self):
- return self.ring(self.data*self.ring.ring.convert(-1))
- def __sub__(self, om):
- return self.__add__(-om)
- def __rsub__(self, om):
- return (-self).__add__(om)
- def __mul__(self, o):
- if not isinstance(o, self.__class__):
- try:
- o = self.ring.convert(o)
- except (NotImplementedError, CoercionFailed):
- return NotImplemented
- return self.ring(self.data*o.data)
- __rmul__ = __mul__
- def __rtruediv__(self, o):
- return self.ring.revert(self)*o
- def __truediv__(self, o):
- if not isinstance(o, self.__class__):
- try:
- o = self.ring.convert(o)
- except (NotImplementedError, CoercionFailed):
- return NotImplemented
- return self.ring.revert(o)*self
- def __pow__(self, oth):
- if oth < 0:
- return self.ring.revert(self) ** -oth
- return self.ring(self.data ** oth)
- def __eq__(self, om):
- if not isinstance(om, self.__class__) or om.ring != self.ring:
- return False
- return self.ring.is_zero(self - om)
- def __ne__(self, om):
- return not self == om
- class QuotientRing(Ring):
- """
- Class representing (commutative) quotient rings.
- You should not usually instantiate this by hand, instead use the constructor
- from the base ring in the construction.
- >>> from sympy.abc import x
- >>> from sympy import QQ
- >>> I = QQ.old_poly_ring(x).ideal(x**3 + 1)
- >>> QQ.old_poly_ring(x).quotient_ring(I)
- QQ[x]/<x**3 + 1>
- Shorter versions are possible:
- >>> QQ.old_poly_ring(x)/I
- QQ[x]/<x**3 + 1>
- >>> QQ.old_poly_ring(x)/[x**3 + 1]
- QQ[x]/<x**3 + 1>
- Attributes:
- - ring - the base ring
- - base_ideal - the ideal used to form the quotient
- """
- has_assoc_Ring = True
- has_assoc_Field = False
- dtype = QuotientRingElement
- def __init__(self, ring, ideal):
- if not ideal.ring == ring:
- raise ValueError('Ideal must belong to %s, got %s' % (ring, ideal))
- self.ring = ring
- self.base_ideal = ideal
- self.zero = self(self.ring.zero)
- self.one = self(self.ring.one)
- def __str__(self):
- return str(self.ring) + "/" + str(self.base_ideal)
- def __hash__(self):
- return hash((self.__class__.__name__, self.dtype, self.ring, self.base_ideal))
- def new(self, a):
- """Construct an element of ``self`` domain from ``a``. """
- if not isinstance(a, self.ring.dtype):
- a = self.ring(a)
- # TODO optionally disable reduction?
- return self.dtype(self, self.base_ideal.reduce_element(a))
- def __eq__(self, other):
- """Returns ``True`` if two domains are equivalent. """
- return isinstance(other, QuotientRing) and \
- self.ring == other.ring and self.base_ideal == other.base_ideal
- def from_ZZ(K1, a, K0):
- """Convert a Python ``int`` object to ``dtype``. """
- return K1(K1.ring.convert(a, K0))
- from_ZZ_python = from_ZZ
- from_QQ_python = from_ZZ_python
- from_ZZ_gmpy = from_ZZ_python
- from_QQ_gmpy = from_ZZ_python
- from_RealField = from_ZZ_python
- from_GlobalPolynomialRing = from_ZZ_python
- from_FractionField = from_ZZ_python
- def from_sympy(self, a):
- return self(self.ring.from_sympy(a))
- def to_sympy(self, a):
- return self.ring.to_sympy(a.data)
- def from_QuotientRing(self, a, K0):
- if K0 == self:
- return a
- def poly_ring(self, *gens):
- """Returns a polynomial ring, i.e. ``K[X]``. """
- raise NotImplementedError('nested domains not allowed')
- def frac_field(self, *gens):
- """Returns a fraction field, i.e. ``K(X)``. """
- raise NotImplementedError('nested domains not allowed')
- def revert(self, a):
- """
- Compute a**(-1), if possible.
- """
- I = self.ring.ideal(a.data) + self.base_ideal
- try:
- return self(I.in_terms_of_generators(1)[0])
- except ValueError: # 1 not in I
- raise NotReversible('%s not a unit in %r' % (a, self))
- def is_zero(self, a):
- return self.base_ideal.contains(a.data)
- def free_module(self, rank):
- """
- Generate a free module of rank ``rank`` over ``self``.
- >>> from sympy.abc import x
- >>> from sympy import QQ
- >>> (QQ.old_poly_ring(x)/[x**2 + 1]).free_module(2)
- (QQ[x]/<x**2 + 1>)**2
- """
- return FreeModuleQuotientRing(self, rank)
|