Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/structure/parent_gens.pyx
4069 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: M = FreeModule(ZZ, 4)
51
sage: M
52
Ambient free module of rank 4 over the principal ideal domain Integer Ring
53
sage: M.ngens()
54
4
55
sage: M.gen(0)
56
(1, 0, 0, 0)
57
sage: M.gens()
58
((1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1))
59
"""
60
61
###############################################################################
62
# Sage: System for Algebra and Geometry Experimentation
63
# Copyright (C) 2005, 2006 William Stein <[email protected]>
64
# Distributed under the terms of the GNU General Public License (GPL)
65
# The full text of the GPL is available at:
66
# http://www.gnu.org/licenses/
67
###############################################################################
68
69
import sage.misc.defaults
70
from sage.misc.latex import latex_variable_name
71
import gens_py
72
cimport parent
73
74
include '../ext/stdsage.pxi'
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
def is_ParentWithGens(x):
81
"""
82
Return True if x is a parent object with generators, i.e., derives from
83
:class:`sage.structure.parent_gens.ParentWithGens` and False otherwise.
84
85
EXAMPLES::
86
87
sage: from sage.structure.parent_gens import is_ParentWithGens
88
sage: is_ParentWithGens(QQ['x'])
89
True
90
sage: is_ParentWithGens(CC)
91
True
92
sage: is_ParentWithGens(Primes())
93
False
94
"""
95
return PY_TYPE_CHECK(x, ParentWithGens)
96
97
def is_ParentWithAdditiveAbelianGens(x):
98
"""
99
Return True if x is a parent object with additive abelian generators, i.e.,
100
derives from
101
:mod:`sage.structure.parent_gens.ParentWithAdditiveAbelianGens` and False
102
otherwise.
103
104
EXAMPLES::
105
106
sage: from sage.structure.parent_gens import is_ParentWithAdditiveAbelianGens
107
sage: is_ParentWithAdditiveAbelianGens(QQ)
108
False
109
sage: is_ParentWithAdditiveAbelianGens(QQ^3)
110
True
111
"""
112
return PY_TYPE_CHECK(x, ParentWithAdditiveAbelianGens)
113
114
def is_ParentWithMultiplicativeAbelianGens(x):
115
"""
116
Return True if x is a parent object with additive abelian generators, i.e.,
117
derives from
118
:class:`sage.structure.parent_gens.ParentWithMultiplicativeAbelianGens` and
119
False otherwise.
120
121
EXAMPLES::
122
123
sage: from sage.structure.parent_gens import is_ParentWithMultiplicativeAbelianGens
124
sage: is_ParentWithMultiplicativeAbelianGens(QQ)
125
False
126
sage: is_ParentWithMultiplicativeAbelianGens(DirichletGroup(11))
127
True
128
"""
129
return PY_TYPE_CHECK(x, ParentWithMultiplicativeAbelianGens)
130
131
def _certify_names(names):
132
v = []
133
try:
134
names = tuple(names)
135
except TypeError:
136
names = [str(names)]
137
for N in names:
138
if not isinstance(N, str):
139
N = str(N)
140
N = N.strip().strip("'")
141
if len(N) == 0:
142
raise ValueError, "variable name must be nonempty"
143
if not N.isalnum() and not N.replace("_","").isalnum():
144
# We must be alphanumeric, but we make an exception for non-leading '_' characters.
145
raise ValueError, "variable names must be alphanumeric, but one is '%s' which is not."%N
146
if not N[0].isalpha():
147
raise ValueError, "first letter of variable name must be a letter"
148
v.append(N)
149
return tuple(v)
150
151
def normalize_names(int ngens, names=None):
152
r"""
153
Return a tuple of strings of variable names of length ngens given the input names.
154
155
INPUT:
156
157
- ``ngens`` - integer
158
159
- ``names``
160
161
- tuple or list of strings, such as ('x', 'y')
162
163
- a string prefix, such as 'alpha'
164
165
- string of single character names, such as 'xyz'
166
167
EXAMPLES::
168
169
sage: from sage.structure.parent_gens import normalize_names as nn
170
sage: nn(1, 'a')
171
('a',)
172
sage: nn(2, 'zzz')
173
('zzz0', 'zzz1')
174
sage: nn(2, 'ab')
175
('a', 'b')
176
sage: nn(3, ('a', 'bb', 'ccc'))
177
('a', 'bb', 'ccc')
178
sage: nn(4, ['a1', 'a2', 'b1', 'b11'])
179
('a1', 'a2', 'b1', 'b11')
180
181
TESTS::
182
183
sage: nn(2, 'z1')
184
('z10', 'z11')
185
sage: PolynomialRing(QQ, 2, 'alpha0')
186
Multivariate Polynomial Ring in alpha00, alpha01 over Rational Field
187
"""
188
if names is None:
189
return None
190
if isinstance(names, str) and names.find(',') != -1:
191
names = names.split(',')
192
if isinstance(names, str) and ngens > 1 and len(names) == ngens:
193
maybe_names = tuple(names)
194
try:
195
_certify_names(maybe_names)
196
names = maybe_names
197
except ValueError:
198
# this happens when you try for 2 names starting "x0"
199
# that gets split to "x", "0" and fails the certification
200
pass
201
if isinstance(names, str):
202
name = names
203
names = sage.misc.defaults.variable_names(ngens, name)
204
names = _certify_names(names)
205
else:
206
names = _certify_names(names)
207
if not isinstance(names, (list, tuple)):
208
raise TypeError, "names must be a list or tuple of strings"
209
for x in names:
210
if not isinstance(x,str):
211
raise TypeError, "names must consist of strings"
212
if ngens != 0 and len(names) != ngens:
213
raise IndexError, "the number of names must equal the number of generators"
214
return names
215
216
# Classes that derive from ParentWithGens must define gen(i) and
217
# ngens() functions. It is also good if they define gens() to return
218
# all gens, but this is not necessary.
219
220
## def make_parent_gens_v0(_class, _dict,
221
## base, has_coerce_map_from, names):
222
## """
223
## This should work for any Python class deriving from this, as long
224
## as it doesn't implement some screwy __new__() method.
225
## """
226
## cdef ParentWithGens new_object
227
## new_object = _class.__new__(_class)
228
## if base is None:
229
## new_object._base = new_object
230
## else:
231
## new_object._base = base
232
## new_object._has_coerce_map_from = has_coerce_map_from
233
## new_object._names = names
234
## if not _dict is None:
235
## new_object.__dict__ = _dict
236
## return new_object
237
238
cdef class ParentWithGens(parent_base.ParentWithBase):
239
# Derived class *must* call __init__ and set the base!
240
def __init__(self, base, names=None, normalize=True, category = None):
241
"""
242
EXAMPLES::
243
244
sage: class MyParent(ParentWithGens):
245
... def ngens(self): return 3
246
sage: P = MyParent(base = QQ, names = 'a,b,c', normalize = True, category = Groups())
247
sage: P.category()
248
Category of groups
249
sage: P._names
250
('a', 'b', 'c')
251
"""
252
self._base = base
253
self._has_coerce_map_from = {}
254
self._assign_names(names=names, normalize=normalize)
255
256
# Why does not this call ParentWithBase.__init__ ?
257
parent_base.ParentWithBase.__init__(self, base, category=category)
258
#if category is not None:
259
# self._init_category_(category)
260
261
## def x__reduce__(self):
262
## if self._base is self:
263
## base = None
264
## else:
265
## base = self._base
266
## if HAS_DICTIONARY(self):
267
## _dict = self.__dict__
268
## else:
269
## _dict = None
270
## return (make_parent_gens_v0, (self.__class__,
271
## _dict, base,
272
## self._has_coerce_map_from,
273
## self._names))
274
275
# Derived class *must* define ngens method.
276
def ngens(self):
277
check_old_coerce(self)
278
raise NotImplementedError, "Number of generators not known."
279
280
# Derived class *must* define gen method.
281
def gen(self, i=0):
282
check_old_coerce(self)
283
raise NotImplementedError, "i-th generator not known."
284
285
def gens(self):
286
"""
287
Return a tuple whose entries are the generators for this
288
object, in order.
289
"""
290
cdef int i, n
291
if self._gens != None:
292
return self._gens
293
else:
294
v = []
295
n = self.ngens()
296
for i from 0 <= i < n:
297
v.append(self.gen(i))
298
self._gens = tuple(v)
299
return self._gens
300
301
def _assign_names(self, names=None, normalize=True):
302
"""
303
Set the names of the generator of this object.
304
305
This can only be done once because objects with generators
306
are immutable, and is typically done during creation of the object.
307
308
309
EXAMPLES:
310
311
When we create this polynomial ring, self._assign_names is called by the constructor:
312
313
::
314
315
sage: R = QQ['x,y,abc']; R
316
Multivariate Polynomial Ring in x, y, abc over Rational Field
317
sage: R.2
318
abc
319
320
We can't rename the variables::
321
322
sage: R._assign_names(['a','b','c'])
323
Traceback (most recent call last):
324
...
325
ValueError: variable names cannot be changed after object creation.
326
"""
327
if self._element_constructor is not None:
328
return parent.Parent._assign_names(self, names=names, normalize=normalize)
329
if names is None: return
330
if normalize:
331
names = normalize_names(self.ngens(), names)
332
if self._names is not None and names != self._names:
333
raise ValueError, 'variable names cannot be changed after object creation.'
334
if isinstance(names, str):
335
names = (names, ) # make it a tuple
336
elif not PY_TYPE_CHECK(names, tuple):
337
raise TypeError, "names must be a tuple of strings"
338
self._names = names
339
340
#################################################################################
341
# Give all objects with generators a dictionary, so that attribute setting
342
# works. It would be nice if this functionality were standard in Pyrex,
343
# i.e., just define __dict__ as an attribute and all this code gets generated.
344
#################################################################################
345
def __getstate__(self):
346
if self._element_constructor is not None:
347
return parent.Parent.__getstate__(self)
348
d = []
349
try:
350
d = list(self.__dict__.copy().iteritems()) # so we can add elements
351
except AttributeError:
352
pass
353
d = dict(d)
354
d['_base'] = self._base
355
d['_gens'] = self._gens
356
d['_gens_dict'] = self._gens_dict
357
d['_list'] = self._list
358
d['_names'] = self._names
359
d['_latex_names'] = self._latex_names
360
try:
361
d['_generator_orders'] = self._generator_orders
362
except AttributeError:
363
pass
364
365
return d
366
367
def __setstate__(self, d):
368
if d.has_key('_element_constructor'):
369
return parent.Parent.__setstate__(self, d)
370
try:
371
self.__dict__.update(d)
372
self._generator_orders = d['_generator_orders']
373
except (AttributeError,KeyError):
374
pass
375
self._base = d['_base']
376
self._gens = d['_gens']
377
self._gens_dict = d['_gens_dict']
378
self._list = d['_list']
379
self._names = d['_names']
380
self._latex_names = d['_latex_names']
381
382
383
#################################################################################
384
# Morphisms of objects with generators
385
#################################################################################
386
387
def hom(self, im_gens, codomain=None, check=True):
388
r"""
389
Return the unique homomorphism from self to codomain that
390
sends ``self.gens()`` to the entries of ``im_gens``.
391
Raises a TypeError if there is no such homomorphism.
392
393
INPUT:
394
395
- ``im_gens`` - the images in the codomain of the generators of
396
this object under the homomorphism
397
398
- ``codomain`` - the codomain of the homomorphism
399
400
- ``check`` - whether to verify that the images of generators extend
401
to define a map (using only canonical coercions).
402
403
OUTPUT:
404
405
- a homomorphism self --> codomain
406
407
.. note::
408
409
As a shortcut, one can also give an object X instead of
410
``im_gens``, in which case return the (if it exists)
411
natural map to X.
412
413
EXAMPLE: Polynomial Ring
414
We first illustrate construction of a few homomorphisms
415
involving a polynomial ring.
416
417
::
418
419
sage: R.<x> = PolynomialRing(ZZ)
420
sage: f = R.hom([5], QQ)
421
sage: f(x^2 - 19)
422
6
423
424
sage: R.<x> = PolynomialRing(QQ)
425
sage: f = R.hom([5], GF(7))
426
Traceback (most recent call last):
427
...
428
TypeError: images do not define a valid homomorphism
429
430
sage: R.<x> = PolynomialRing(GF(7))
431
sage: f = R.hom([3], GF(49,'a'))
432
sage: f
433
Ring morphism:
434
From: Univariate Polynomial Ring in x over Finite Field of size 7
435
To: Finite Field in a of size 7^2
436
Defn: x |--> 3
437
sage: f(x+6)
438
2
439
sage: f(x^2+1)
440
3
441
442
EXAMPLE: Natural morphism
443
444
::
445
446
sage: f = ZZ.hom(GF(5))
447
sage: f(7)
448
2
449
sage: f
450
Ring Coercion morphism:
451
From: Integer Ring
452
To: Finite Field of size 5
453
454
There might not be a natural morphism, in which case a TypeError exception is raised.
455
456
::
457
458
sage: QQ.hom(ZZ)
459
Traceback (most recent call last):
460
...
461
TypeError: Natural coercion morphism from Rational Field to Integer Ring not defined.
462
"""
463
if self._element_constructor is not None:
464
return parent.Parent.hom(self, im_gens, codomain, check)
465
if isinstance(im_gens, parent.Parent):
466
return self.Hom(im_gens).natural_map()
467
if codomain is None:
468
from sage.structure.all import Sequence
469
im_gens = Sequence(im_gens)
470
codomain = im_gens.universe()
471
return self.Hom(codomain)(im_gens, check=check)
472
473
474
cdef class ParentWithMultiplicativeAbelianGens(ParentWithGens):
475
def generator_orders(self):
476
check_old_coerce(self)
477
if self._generator_orders != None:
478
return self._generator_orders
479
else:
480
g = []
481
for x in self.gens():
482
g.append(x.multiplicative_order())
483
self._generator_orders = g
484
return g
485
486
def __iter__(self):
487
"""
488
Return an iterator over the elements in this object.
489
"""
490
return gens_py.multiplicative_iterator(self)
491
492
493
494
cdef class ParentWithAdditiveAbelianGens(ParentWithGens):
495
def generator_orders(self):
496
check_old_coerce(self)
497
if self._generator_orders != None:
498
return self._generator_orders
499
else:
500
g = []
501
for x in self.gens():
502
g.append(x.additive_order())
503
self._generator_orders = g
504
return g
505
506
def __iter__(self):
507
"""
508
Return an iterator over the elements in this object.
509
"""
510
return gens_py.abelian_iterator(self)
511
512
513
514
515
class localvars:
516
r"""
517
Context manager for safely temporarily changing the variables
518
names of an object with generators.
519
520
Objects with named generators are globally unique in Sage.
521
Sometimes, though, it is very useful to be able to temporarily
522
display the generators differently. The new Python ``with``
523
statement and the localvars context manager make this easy and
524
safe (and fun!)
525
526
Suppose X is any object with generators. Write
527
528
::
529
530
with localvars(X, names[, latex_names] [,normalize=False]):
531
some code
532
...
533
534
and the indented code will be run as if the names in X are changed
535
to the new names. If you give normalize=True, then the names are
536
assumed to be a tuple of the correct number of strings.
537
538
If you're writing Python library code, you currently have
539
to put ``from __future__ import with_statement`` in your file
540
in order to use the ``with`` statement. This restriction will
541
disappear in Python 2.6.
542
543
EXAMPLES::
544
545
sage: R.<x,y> = PolynomialRing(QQ,2)
546
sage: with localvars(R, 'z,w'):
547
... print x^3 + y^3 - x*y
548
...
549
z^3 + w^3 - z*w
550
551
.. note::
552
553
I wrote this because it was needed to print elements of the
554
quotient of a ring R by an ideal I using the print function for
555
elements of R. See the code in
556
``quotient_ring_element.pyx``.
557
558
AUTHOR:
559
560
- William Stein (2006-10-31)
561
"""
562
def __init__(self, obj, names, latex_names=None, normalize=True):
563
self._obj = obj
564
if normalize:
565
self._names = normalize_names(obj.ngens(), names)
566
self._latex_names = latex_names
567
else:
568
self._names = names
569
self._latex_names = latex_names
570
571
def __enter__(self):
572
self._orig = self._obj.__temporarily_change_names(self._names, self._latex_names)
573
574
def __exit__(self, type, value, traceback):
575
self._obj.__temporarily_change_names(self._orig[0], self._orig[1])
576
577
578
579