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