| 1 | include '../../ext/stdsage.pxi' |
|---|
| 2 | |
|---|
| 3 | from sage.structure.parent_gens cimport ParentWithGens |
|---|
| 4 | import sage.misc.latex |
|---|
| 5 | import multi_polynomial_ideal |
|---|
| 6 | from term_order import TermOrder |
|---|
| 7 | from sage.rings.integer_ring import ZZ |
|---|
| 8 | from sage.rings.polynomial.polydict import PolyDict |
|---|
| 9 | import multi_polynomial_element |
|---|
| 10 | |
|---|
| 11 | def is_MPolynomialRing(x): |
|---|
| 12 | return bool(PY_TYPE_CHECK(x, MPolynomialRing_generic)) |
|---|
| 13 | |
|---|
| 14 | cdef class MPolynomialRing_generic(sage.rings.ring.CommutativeRing): |
|---|
| 15 | def __init__(self, base_ring, n, names, order): |
|---|
| 16 | order = TermOrder(order,n) |
|---|
| 17 | if not isinstance(base_ring, sage.rings.ring.CommutativeRing): |
|---|
| 18 | raise TypeError, "Base ring must be a commutative ring." |
|---|
| 19 | n = int(n) |
|---|
| 20 | if n < 0: |
|---|
| 21 | raise ValueError, "Multivariate Polynomial Rings must " + \ |
|---|
| 22 | "have more than 0 variables." |
|---|
| 23 | self.__ngens = n |
|---|
| 24 | self.__term_order = order |
|---|
| 25 | self._has_singular = False #cannot convert to Singular by default |
|---|
| 26 | ParentWithGens.__init__(self, base_ring, names) |
|---|
| 27 | |
|---|
| 28 | def is_integral_domain(self): |
|---|
| 29 | return self.base_ring().is_integral_domain() |
|---|
| 30 | |
|---|
| 31 | cdef _coerce_c_impl(self, x): |
|---|
| 32 | """ |
|---|
| 33 | Return the canonical coercion of x to this multivariate |
|---|
| 34 | polynomial ring, if one is defined, or raise a TypeError. |
|---|
| 35 | |
|---|
| 36 | The rings that canonically coerce to this polynomial ring are: |
|---|
| 37 | * this ring itself |
|---|
| 38 | * polynomial rings in the same variables over any base ring that |
|---|
| 39 | canonically coerces to the base ring of this ring |
|---|
| 40 | * any ring that canonically coerces to the base ring of this |
|---|
| 41 | polynomial ring. |
|---|
| 42 | |
|---|
| 43 | TESTS: |
|---|
| 44 | This fairly complicated code (from Michel Vandenbergh) ends up |
|---|
| 45 | imlicitly calling _coerce_c_impl: |
|---|
| 46 | sage: z = polygen(QQ, 'z') |
|---|
| 47 | sage: W.<s>=NumberField(z^2+1) |
|---|
| 48 | sage: Q.<u,v,w> = W[] |
|---|
| 49 | sage: W1 = FractionField (Q) |
|---|
| 50 | sage: S.<x,y,z> = W1[] |
|---|
| 51 | sage: u + x |
|---|
| 52 | x + u |
|---|
| 53 | sage: x + 1/u |
|---|
| 54 | x + 1/u |
|---|
| 55 | """ |
|---|
| 56 | try: |
|---|
| 57 | P = x.parent() |
|---|
| 58 | # polynomial rings in the same variable over the any base that coerces in: |
|---|
| 59 | if is_MPolynomialRing(P): |
|---|
| 60 | if P.variable_names() == self.variable_names(): |
|---|
| 61 | if self.has_coerce_map_from(P.base_ring()): |
|---|
| 62 | return self(x) |
|---|
| 63 | |
|---|
| 64 | except AttributeError: |
|---|
| 65 | pass |
|---|
| 66 | |
|---|
| 67 | # any ring that coerces to the base ring of this polynomial ring. |
|---|
| 68 | return self._coerce_try(x, [self.base_ring()]) |
|---|
| 69 | |
|---|
| 70 | def __richcmp__(left, right, int op): |
|---|
| 71 | return (<ParentWithGens>left)._richcmp(right, op) |
|---|
| 72 | |
|---|
| 73 | cdef int _cmp_c_impl(left, Parent right) except -2: |
|---|
| 74 | if not is_MPolynomialRing(right): |
|---|
| 75 | return cmp(type(left),type(right)) |
|---|
| 76 | else: |
|---|
| 77 | return cmp((left.base_ring(), left.__ngens, left.variable_names(), left.__term_order), |
|---|
| 78 | (right.base_ring(), (<MPolynomialRing_generic>right).__ngens, right.variable_names(), (<MPolynomialRing_generic>right).__term_order)) |
|---|
| 79 | |
|---|
| 80 | def __contains__(self, x): |
|---|
| 81 | """ |
|---|
| 82 | This definition of containment does not involve a natural |
|---|
| 83 | inclusion from rings with less variables into rings with more. |
|---|
| 84 | """ |
|---|
| 85 | try: |
|---|
| 86 | return x.parent() == self |
|---|
| 87 | except AttributeError: |
|---|
| 88 | return False |
|---|
| 89 | |
|---|
| 90 | def _repr_(self): |
|---|
| 91 | return "Polynomial Ring in %s over %s"%(", ".join(self.variable_names()), self.base_ring()) |
|---|
| 92 | |
|---|
| 93 | def _latex_(self): |
|---|
| 94 | vars = str(self.latex_variable_names()).replace('\n','').replace("'",'') |
|---|
| 95 | return "%s[%s]"%(sage.misc.latex.latex(self.base_ring()), vars[1:-1]) |
|---|
| 96 | |
|---|
| 97 | |
|---|
| 98 | def _ideal_class_(self): |
|---|
| 99 | return multi_polynomial_ideal.MPolynomialIdeal |
|---|
| 100 | |
|---|
| 101 | def _is_valid_homomorphism_(self, codomain, im_gens): |
|---|
| 102 | try: |
|---|
| 103 | # all that is needed is that elements of the base ring |
|---|
| 104 | # of the polynomial ring canonically coerce into codomain. |
|---|
| 105 | # Since poly rings are free, any image of the gen |
|---|
| 106 | # determines a homomorphism |
|---|
| 107 | codomain._coerce_(self.base_ring()(1)) |
|---|
| 108 | except TypeError: |
|---|
| 109 | return False |
|---|
| 110 | return True |
|---|
| 111 | |
|---|
| 112 | def _magma_(self, magma=None): |
|---|
| 113 | """ |
|---|
| 114 | Used in converting this ring to the corresponding ring in MAGMA. |
|---|
| 115 | |
|---|
| 116 | EXAMPLES: |
|---|
| 117 | sage: R.<y,z,w> = PolynomialRing(QQ,3) |
|---|
| 118 | sage: magma(R) # optional |
|---|
| 119 | Polynomial ring of rank 3 over Rational Field |
|---|
| 120 | Graded Reverse Lexicographical Order |
|---|
| 121 | Variables: y, z, w |
|---|
| 122 | |
|---|
| 123 | sage: magma(PolynomialRing(GF(7),4, 'x')) #optional |
|---|
| 124 | Polynomial ring of rank 4 over GF(7) |
|---|
| 125 | Graded Reverse Lexicographical Order |
|---|
| 126 | Variables: x0, x1, x2, x3 |
|---|
| 127 | |
|---|
| 128 | sage: magma(PolynomialRing(GF(49,'a'),10, 'x')) #optional |
|---|
| 129 | Polynomial ring of rank 10 over GF(7^2) |
|---|
| 130 | Graded Reverse Lexicographical Order |
|---|
| 131 | Variables: x0, x1, x2, x3, x4, x5, x6, x7, x8, x9 |
|---|
| 132 | |
|---|
| 133 | sage: magma(PolynomialRing(ZZ['a,b,c'],3, 'x')) #optional |
|---|
| 134 | Polynomial ring of rank 3 over Polynomial ring of rank 3 over Integer Ring |
|---|
| 135 | Graded Reverse Lexicographical Order |
|---|
| 136 | Variables: x0, x1, x2 |
|---|
| 137 | """ |
|---|
| 138 | if magma == None: |
|---|
| 139 | import sage.interfaces.magma |
|---|
| 140 | magma = sage.interfaces.magma.magma |
|---|
| 141 | |
|---|
| 142 | try: |
|---|
| 143 | if self.__magma is None: |
|---|
| 144 | raise AttributeError |
|---|
| 145 | m = self.__magma |
|---|
| 146 | m._check_valid() |
|---|
| 147 | if not m.parent() is magma: |
|---|
| 148 | raise ValueError |
|---|
| 149 | return m |
|---|
| 150 | except (AttributeError,ValueError): |
|---|
| 151 | B = magma(self.base_ring()) |
|---|
| 152 | R = magma('PolynomialRing(%s, %s, %s)'%(B.name(), self.ngens(),self.term_order().magma_str())) |
|---|
| 153 | R.assign_names(self.variable_names()) |
|---|
| 154 | self.__magma = R |
|---|
| 155 | return R |
|---|
| 156 | |
|---|
| 157 | def _magma_init_(self): |
|---|
| 158 | B = self.base_ring()._magma_init_() |
|---|
| 159 | R = 'PolynomialRing(%s, %s, %s)'%(B, self.ngens(),self.term_order().magma_str()) |
|---|
| 160 | return R |
|---|
| 161 | |
|---|
| 162 | def is_finite(self): |
|---|
| 163 | if self.ngens() == 0: |
|---|
| 164 | return self.base_ring().is_finite() |
|---|
| 165 | return False |
|---|
| 166 | |
|---|
| 167 | def is_field(self): |
|---|
| 168 | """ |
|---|
| 169 | Return True if this multivariate polynomial ring is a field, i.e., |
|---|
| 170 | it is a ring in 0 generators over a field. |
|---|
| 171 | """ |
|---|
| 172 | if self.ngens() == 0: |
|---|
| 173 | return self.base_ring().is_field() |
|---|
| 174 | return False |
|---|
| 175 | |
|---|
| 176 | def term_order(self): |
|---|
| 177 | return self.__term_order |
|---|
| 178 | |
|---|
| 179 | def characteristic(self): |
|---|
| 180 | """ |
|---|
| 181 | Return the characteristic of this polynomial ring. |
|---|
| 182 | |
|---|
| 183 | EXAMPLES: |
|---|
| 184 | sage: R = MPolynomialRing(QQ, 'x', 3) |
|---|
| 185 | sage: R.characteristic() |
|---|
| 186 | 0 |
|---|
| 187 | sage: R = MPolynomialRing(GF(7),'x', 20) |
|---|
| 188 | sage: R.characteristic() |
|---|
| 189 | 7 |
|---|
| 190 | """ |
|---|
| 191 | return self.base_ring().characteristic() |
|---|
| 192 | |
|---|
| 193 | def gen(self, n=0): |
|---|
| 194 | if n < 0 or n >= self.__ngens: |
|---|
| 195 | raise ValueError, "Generator not defined." |
|---|
| 196 | return self._gens[int(n)] |
|---|
| 197 | |
|---|
| 198 | def gens(self): |
|---|
| 199 | return self._gens |
|---|
| 200 | |
|---|
| 201 | def krull_dimension(self): |
|---|
| 202 | return self.base_ring().krull_dimension() + self.ngens() |
|---|
| 203 | |
|---|
| 204 | def ngens(self): |
|---|
| 205 | return self.__ngens |
|---|
| 206 | |
|---|
| 207 | def _monomial_order_function(self): |
|---|
| 208 | raise NotImplementedError |
|---|
| 209 | |
|---|
| 210 | def latex_variable_names(self): |
|---|
| 211 | """ |
|---|
| 212 | Returns the list of variable names suitable for latex output. |
|---|
| 213 | |
|---|
| 214 | All '_SOMETHING' substrings are replaced by '_{SOMETHING}' recursively |
|---|
| 215 | so that subscripts of subscripts work. |
|---|
| 216 | |
|---|
| 217 | EXAMPLES: |
|---|
| 218 | sage: R, x = PolynomialRing(QQ,'x',12).objgens() |
|---|
| 219 | sage: x |
|---|
| 220 | (x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11) |
|---|
| 221 | sage: print R.latex_variable_names () |
|---|
| 222 | ['x_{0}', 'x_{1}', 'x_{2}', 'x_{3}', 'x_{4}', 'x_{5}', 'x_{6}', 'x_{7}', 'x_{8}', 'x_{9}', 'x_{10}', 'x_{11}'] |
|---|
| 223 | sage: f = x[0]^3 + 15/3 * x[1]^10 |
|---|
| 224 | sage: print latex(f) |
|---|
| 225 | 5 x_{1}^{10} + x_{0}^{3} |
|---|
| 226 | """ |
|---|
| 227 | if self._latex_names is not None: |
|---|
| 228 | return self._latex_names |
|---|
| 229 | names = [] |
|---|
| 230 | for g in self.variable_names(): |
|---|
| 231 | i = len(g)-1 |
|---|
| 232 | while i >= 0 and g[i].isdigit(): |
|---|
| 233 | i -= 1 |
|---|
| 234 | if i < len(g)-1: |
|---|
| 235 | g = '%s_{%s}'%(g[:i+1], g[i+1:]) |
|---|
| 236 | names.append(g) |
|---|
| 237 | self._latex_names = names |
|---|
| 238 | return names |
|---|
| 239 | |
|---|
| 240 | def __reduce__(self): |
|---|
| 241 | """ |
|---|
| 242 | """ |
|---|
| 243 | |
|---|
| 244 | base_ring = self.base_ring() |
|---|
| 245 | n = self.ngens() |
|---|
| 246 | names = self.variable_names() |
|---|
| 247 | order = self.term_order() |
|---|
| 248 | |
|---|
| 249 | return unpickle_MPolynomialRing_generic_v1,(base_ring, n, names, order) |
|---|
| 250 | |
|---|
| 251 | |
|---|
| 252 | def random_element(self, degree=2, terms=5, *args, **kwds): |
|---|
| 253 | r""" |
|---|
| 254 | Return a random polynomial in this polynomial ring. |
|---|
| 255 | |
|---|
| 256 | INPUT: |
|---|
| 257 | degree -- maximum total degree of resulting polynomial |
|---|
| 258 | terms -- maximum number of terms to generate |
|---|
| 259 | |
|---|
| 260 | OUTPUT: a random polynomial of total degree \code{degree} |
|---|
| 261 | and with \code{term} terms in it. |
|---|
| 262 | |
|---|
| 263 | EXAMPLES: |
|---|
| 264 | sage: [QQ['x,y'].random_element() for _ in range(5)] |
|---|
| 265 | [-1/14*x*y + 1/2*x, x*y + x - y + 1, 3*x*y + x - 1/2, 1/3*x*y - 5*x + 1/2*y + 7/6, 2*x*y + 1/2*x + 1] |
|---|
| 266 | sage: R = MPolynomialRing(ZZ, 'x,y',2 ); |
|---|
| 267 | sage: R.random_element(2) # random |
|---|
| 268 | -1*x*y + x + 15*y - 2 |
|---|
| 269 | sage: R.random_element(12) # random |
|---|
| 270 | x^4*y^5 + x^3*y^5 + 6*x^2*y^2 - x^2 |
|---|
| 271 | sage: R.random_element(12,3) # random |
|---|
| 272 | -3*x^4*y^2 - x^5 - x^4*y |
|---|
| 273 | sage: R.random_element(3) # random |
|---|
| 274 | 2*y*z + 2*x + 2*y |
|---|
| 275 | |
|---|
| 276 | sage: R.<x,y> = MPolynomialRing(RR) |
|---|
| 277 | sage: R.random_element(2) # random |
|---|
| 278 | -0.645358174399450*x*y + 0.572655401740132*x + 0.197478565033010 |
|---|
| 279 | |
|---|
| 280 | sage: R.random_element(41) # random |
|---|
| 281 | -4*x^6*y^4*z^4*a^6*b^3*c^6*d^5 + 1/2*x^4*y^3*z^5*a^4*c^5*d^6 - 5*x^3*z^3*a^6*b^4*c*d^5 + 10*x^2*y*z^5*a^4*b^2*c^3*d^4 - 5*x^3*y^5*z*b^2*c^5*d |
|---|
| 282 | |
|---|
| 283 | AUTHOR: |
|---|
| 284 | -- didier deshommes |
|---|
| 285 | """ |
|---|
| 286 | # General strategy: |
|---|
| 287 | # generate n-tuples of numbers with each element in the tuple |
|---|
| 288 | # not greater than (degree/n) so that the degree |
|---|
| 289 | # (ie, the sum of the elements in the tuple) does not exceed |
|---|
| 290 | # their total degree |
|---|
| 291 | |
|---|
| 292 | n = self.__ngens # length of the n-tuple |
|---|
| 293 | max_deg = int(degree/n) # max degree for each term |
|---|
| 294 | R = self.base_ring() |
|---|
| 295 | |
|---|
| 296 | # restrict exponents to positive integers only |
|---|
| 297 | exponents = [ tuple([ZZ.random_element(0,max_deg+1) for _ in range(n)]) |
|---|
| 298 | for _ in range(terms) ] |
|---|
| 299 | coeffs = [] |
|---|
| 300 | for _ in range(terms): |
|---|
| 301 | c = R.random_element(*args,**kwds) |
|---|
| 302 | while not c: |
|---|
| 303 | c = R.random_element(*args,**kwds) |
|---|
| 304 | coeffs.append(c) # allow only nonzero coefficients |
|---|
| 305 | |
|---|
| 306 | d = dict( zip(tuple(exponents), coeffs) ) |
|---|
| 307 | return self(multi_polynomial_element.MPolynomial_polydict(self, PolyDict(d))) |
|---|
| 308 | |
|---|
| 309 | |
|---|
| 310 | #################### |
|---|
| 311 | # Leave *all* old versions! |
|---|
| 312 | |
|---|
| 313 | def unpickle_MPolynomialRing_generic_v1(base_ring, n, names, order): |
|---|
| 314 | from sage.rings.polynomial.multi_polynomial_ring import MPolynomialRing |
|---|
| 315 | return MPolynomialRing(base_ring, n, names=names, order=order) |
|---|
| 316 | |
|---|
| 317 | |
|---|
| 318 | def unpickle_MPolynomialRing_generic(base_ring, n, names, order): |
|---|
| 319 | from sage.rings.polynomial.multi_polynomial_ring import MPolynomialRing |
|---|
| 320 | |
|---|
| 321 | return MPolynomialRing(base_ring, n, names=names, order=order) |
|---|