Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sage
Path: blob/develop/src/sage/structure/parent_gens.pyx
4052 views
1
# sage_setup: distribution = sagemath-objects
2
r"""
3
Base class for old-style parent objects with generators
4
5
.. NOTE::
6
7
This class is being deprecated, see
8
``sage.structure.parent.Parent`` and
9
``sage.structure.category_object.CategoryObject`` for the new
10
model.
11
12
Many parent objects in Sage are equipped with generators, which are
13
special elements of the object. For example, the polynomial ring
14
`\ZZ[x,y,z]` is generated by `x`, `y`, and `z`. In Sage the `i`-th
15
generator of an object ``X`` is obtained using the notation
16
``X.gen(i)``. From the Sage interactive prompt, the shorthand
17
notation ``X.i`` is also allowed.
18
19
REQUIRED: A class that derives from ParentWithGens *must* define
20
the ngens() and gen(i) methods.
21
22
OPTIONAL: It is also good if they define gens() to return all gens,
23
but this is not necessary.
24
25
The ``gens`` function returns a tuple of all generators, the
26
``ngens`` function returns the number of generators.
27
28
The ``_assign_names`` functions is for internal use only, and is
29
called when objects are created to set the generator names. It can
30
only be called once.
31
32
The following examples illustrate these functions in the context of
33
multivariate polynomial rings and free modules.
34
35
EXAMPLES::
36
37
sage: R = PolynomialRing(ZZ, 3, 'x')
38
sage: R.ngens()
39
3
40
sage: R.gen(0)
41
x0
42
sage: R.gens()
43
(x0, x1, x2)
44
sage: R.variable_names()
45
('x0', 'x1', 'x2')
46
47
This example illustrates generators for a free module over `\ZZ`.
48
49
::
50
51
sage: # needs sage.modules
52
sage: M = FreeModule(ZZ, 4)
53
sage: M
54
Ambient free module of rank 4 over the principal ideal domain Integer Ring
55
sage: M.ngens()
56
4
57
sage: M.gen(0)
58
(1, 0, 0, 0)
59
sage: M.gens()
60
((1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1))
61
"""
62
63
# ****************************************************************************
64
# Copyright (C) 2005, 2006 William Stein <[email protected]>
65
#
66
# This program is free software: you can redistribute it and/or modify
67
# it under the terms of the GNU General Public License as published by
68
# the Free Software Foundation, either version 2 of the License, or
69
# (at your option) any later version.
70
# https://www.gnu.org/licenses/
71
# ****************************************************************************
72
73
cimport sage.structure.parent as parent
74
cimport sage.structure.category_object as category_object
75
76
77
cdef inline check_old_coerce(parent.Parent p):
78
if p._element_constructor is not None:
79
raise RuntimeError("%s still using old coercion framework" % p)
80
81
82
cdef class ParentWithGens(ParentWithBase):
83
# Derived class *must* call __init__ and set the base!
84
def __init__(self, base, names=None, normalize=True, category=None):
85
"""
86
EXAMPLES::
87
88
sage: from sage.structure.parent_gens import ParentWithGens
89
sage: class MyParent(ParentWithGens):
90
....: def ngens(self): return 3
91
sage: P = MyParent(base=QQ, names='a,b,c', normalize=True, category=Groups())
92
sage: P.category()
93
Category of groups
94
sage: P._names
95
('a', 'b', 'c')
96
"""
97
self._base = base
98
self._assign_names(names=names, normalize=normalize)
99
100
ParentWithBase.__init__(self, base, category=category)
101
102
# Derived class *must* define ngens method.
103
def ngens(self):
104
check_old_coerce(self)
105
raise NotImplementedError("Number of generators not known.")
106
107
# Derived class *must* define gen method.
108
def gen(self, i=0):
109
check_old_coerce(self)
110
raise NotImplementedError("i-th generator not known.")
111
112
def gens(self):
113
"""
114
Return a tuple whose entries are the generators for this
115
object, in order.
116
"""
117
cdef int i
118
if self._gens is not None:
119
return self._gens
120
self._gens = tuple(self.gen(i) for i in range(self.ngens()))
121
return self._gens
122
123
def _assign_names(self, names=None, normalize=True):
124
"""
125
Set the names of the generator of this object.
126
127
This can only be done once because objects with generators
128
are immutable, and is typically done during creation of the object.
129
130
EXAMPLES:
131
132
When we create this polynomial ring, self._assign_names is called by the constructor:
133
134
::
135
136
sage: R = QQ['x,y,abc']; R
137
Multivariate Polynomial Ring in x, y, abc over Rational Field
138
sage: R.2
139
abc
140
141
We can't rename the variables::
142
143
sage: R._assign_names(['a','b','c'])
144
Traceback (most recent call last):
145
...
146
ValueError: variable names cannot be changed after object creation.
147
"""
148
if self._element_constructor is not None:
149
return parent.Parent._assign_names(self, names=names, normalize=normalize)
150
if names is None: return
151
if normalize:
152
names = category_object.normalize_names(self.ngens(), names)
153
if self._names is not None and names != self._names:
154
raise ValueError('variable names cannot be changed after object creation.')
155
if isinstance(names, str):
156
names = (names, ) # make it a tuple
157
elif not isinstance(names, tuple):
158
raise TypeError("names must be a tuple of strings")
159
self._names = names
160
161
#################################################################################
162
# Give all objects with generators a dictionary, so that attribute setting
163
# works. It would be nice if this functionality were standard in Pyrex,
164
# i.e., just define __dict__ as an attribute and all this code gets generated.
165
#################################################################################
166
def __getstate__(self):
167
if self._element_constructor is not None:
168
return parent.Parent.__getstate__(self)
169
d = []
170
try:
171
d = list(self.__dict__.copy().iteritems()) # so we can add elements
172
except AttributeError:
173
pass
174
d = dict(d)
175
d['_base'] = self._base
176
d['_gens'] = self._gens
177
d['_list'] = self._list
178
d['_names'] = self._names
179
d['_latex_names'] = self._latex_names
180
181
return d
182
183
def __setstate__(self, d):
184
if '_element_constructor' in d:
185
return parent.Parent.__setstate__(self, d)
186
try:
187
self.__dict__.update(d)
188
except (AttributeError,KeyError):
189
pass
190
self._base = d['_base']
191
self._gens = d['_gens']
192
self._list = d['_list']
193
self._names = d['_names']
194
self._latex_names = d['_latex_names']
195
196
######################################################################
197
# Morphisms of objects with generators
198
######################################################################
199
200
def hom(self, im_gens, codomain=None, base_map=None, category=None, check=True):
201
r"""
202
Return the unique homomorphism from ``self`` to codomain that
203
sends ``self.gens()`` to the entries of ``im_gens``
204
and induces the map ``base_map`` on the base ring.
205
206
This raises a :exc:`TypeError` if there is no such homomorphism.
207
208
INPUT:
209
210
- ``im_gens`` -- the images in the codomain of the generators of
211
this object under the homomorphism
212
213
- ``codomain`` -- the codomain of the homomorphism
214
215
- ``base_map`` -- a map from the base ring of the domain into something
216
that coerces into the codomain
217
218
- ``category`` -- the category of the resulting morphism
219
220
- ``check`` -- whether to verify that the images of generators extend
221
to define a map (using only canonical coercions)
222
223
OUTPUT: a homomorphism ``self --> codomain``
224
225
.. NOTE::
226
227
As a shortcut, one can also give an object X instead of
228
``im_gens``, in which case return the (if it exists)
229
natural map to X.
230
231
EXAMPLES: Polynomial Ring
232
We first illustrate construction of a few homomorphisms
233
involving a polynomial ring.
234
235
::
236
237
sage: R.<x> = PolynomialRing(ZZ)
238
sage: f = R.hom([5], QQ)
239
sage: f(x^2 - 19)
240
6
241
242
sage: R.<x> = PolynomialRing(QQ)
243
sage: f = R.hom([5], GF(7))
244
Traceback (most recent call last):
245
...
246
ValueError: relations do not all (canonically) map to 0
247
under map determined by images of generators
248
249
sage: # needs sage.rings.finite_rings
250
sage: R.<x> = PolynomialRing(GF(7))
251
sage: f = R.hom([3], GF(49, 'a'))
252
sage: f
253
Ring morphism:
254
From: Univariate Polynomial Ring in x over Finite Field of size 7
255
To: Finite Field in a of size 7^2
256
Defn: x |--> 3
257
sage: f(x + 6)
258
2
259
sage: f(x^2 + 1)
260
3
261
262
EXAMPLES: Natural morphism
263
264
::
265
266
sage: f = ZZ.hom(GF(5))
267
sage: f(7)
268
2
269
sage: f
270
Natural morphism:
271
From: Integer Ring
272
To: Finite Field of size 5
273
274
There might not be a natural morphism, in which case a
275
:exc:`TypeError` exception is raised.
276
277
::
278
279
sage: QQ.hom(ZZ)
280
Traceback (most recent call last):
281
...
282
TypeError: natural coercion morphism from Rational Field to Integer Ring not defined
283
284
You can specify a map on the base ring::
285
286
sage: # needs sage.rings.finite_rings
287
sage: k = GF(2)
288
sage: R.<a> = k[]
289
sage: l.<a> = k.extension(a^3 + a^2 + 1)
290
sage: R.<b> = l[]
291
sage: m.<b> = l.extension(b^2 + b + a)
292
sage: n.<z> = GF(2^6)
293
sage: m.hom([z^4 + z^3 + 1], base_map=l.hom([z^5 + z^4 + z^2]))
294
Ring morphism:
295
From: Univariate Quotient Polynomial Ring in b over
296
Finite Field in a of size 2^3 with modulus b^2 + b + a
297
To: Finite Field in z of size 2^6
298
Defn: b |--> z^4 + z^3 + 1
299
with map of base ring
300
"""
301
if self._element_constructor is not None:
302
return parent.Parent.hom(self, im_gens, codomain, base_map=base_map, category=category, check=check)
303
if isinstance(im_gens, parent.Parent):
304
return self.Hom(im_gens).natural_map()
305
if codomain is None:
306
from sage.structure.sequence import Sequence
307
im_gens = Sequence(im_gens)
308
codomain = im_gens.universe()
309
kwds = {}
310
if check is not None:
311
kwds['check'] = check
312
if base_map is not None:
313
kwds['base_map'] = base_map
314
Hom_kwds = {} if category is None else {'category': category}
315
return self.Hom(codomain, **Hom_kwds)(im_gens, **kwds)
316
317
318
cdef class localvars:
319
r"""
320
Context manager for safely temporarily changing the variables
321
names of an object with generators.
322
323
Objects with named generators are globally unique in Sage.
324
Sometimes, though, it is very useful to be able to temporarily
325
display the generators differently. The new Python ``with``
326
statement and the localvars context manager make this easy and
327
safe (and fun!)
328
329
Suppose X is any object with generators. Write
330
331
::
332
333
with localvars(X, names[, latex_names] [,normalize=False]):
334
some code
335
...
336
337
and the indented code will be run as if the names in X are changed
338
to the new names. If you give normalize=True, then the names are
339
assumed to be a tuple of the correct number of strings.
340
341
EXAMPLES::
342
343
sage: R.<x,y> = PolynomialRing(QQ, 2)
344
sage: with localvars(R, 'z,w'):
345
....: print(x^3 + y^3 - x*y)
346
z^3 + w^3 - z*w
347
348
.. NOTE::
349
350
I wrote this because it was needed to print elements of the
351
quotient of a ring R by an ideal I using the print function for
352
elements of R. See the code in
353
``quotient_ring_element.pyx``.
354
355
AUTHOR:
356
357
- William Stein (2006-10-31)
358
"""
359
cdef object _obj
360
cdef object _names
361
cdef object _latex_names
362
cdef object _orig
363
364
def __init__(self, obj, names, latex_names=None, normalize=True):
365
self._obj = obj
366
if normalize:
367
self._names = category_object.normalize_names(obj.ngens(), names)
368
self._latex_names = latex_names
369
else:
370
self._names = names
371
self._latex_names = latex_names
372
373
def __enter__(self):
374
self._orig = self._obj._temporarily_change_names(self._names, self._latex_names)
375
376
def __exit__(self, type, value, traceback):
377
self._obj._temporarily_change_names(self._orig[0], self._orig[1])
378
379