Spaces:
Running
Running
File size: 5,911 Bytes
6a86ad5 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 |
"""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
data = self.ring.ring.to_sympy(self.data)
return sstr(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)
|