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