Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/structure/parent_old.pyx
4057 views
1
r"""
2
Base class for old-style parent objects
3
4
CLASS HIERARCHY:
5
6
SageObject
7
Parent
8
ParentWithBase
9
ParentWithGens
10
11
12
TESTS:
13
14
This came up in some subtle bug once.
15
::
16
17
sage: gp(2) + gap(3)
18
5
19
"""
20
21
###############################################################################
22
# Sage: System for Algebra and Geometry Experimentation
23
# Copyright (C) 2006 William Stein <[email protected]>
24
# Distributed under the terms of the GNU General Public License (GPL)
25
# The full text of the GPL is available at:
26
# http://www.gnu.org/licenses/
27
###############################################################################
28
29
cimport sage_object
30
import operator
31
from parent import Set_PythonType, Set_PythonType_class
32
from coerce import py_scalar_parent
33
34
include '../ext/python_object.pxi'
35
include '../ext/python_bool.pxi'
36
include '../ext/stdsage.pxi'
37
38
cdef inline check_old_coerce(Parent p):
39
if p._element_constructor is not None:
40
raise RuntimeError, "%s still using old coercion framework" % p
41
42
43
## def make_parent_v0(_class, _dict, has_coerce_map_from):
44
## """
45
## This should work for any Python class deriving from this, as long
46
## as it doesn't implement some screwy __new__() method.
47
## """
48
## cdef Parent new_object
49
## new_object = _class.__new__(_class)
50
## if not _dict is None:
51
## new_object.__dict__ = _dict
52
## new_object._has_coerce_map_from = has_coerce_map_from
53
## return new_object
54
55
cdef class Parent(parent.Parent):
56
"""
57
Parents are the SAGE/mathematical analogues of container objects
58
in computer science.
59
"""
60
61
def __init__(self, coerce_from=[], actions=[], embeddings=[], category=None):
62
# TODO: many classes don't call this at all, but __new__ crashes Sage
63
# if len(coerce_from) > 0:
64
# print type(self), coerce_from
65
self.init_coerce(False)
66
self._coerce_from_list = list(coerce_from)
67
self._coerce_from_hash = {}
68
self._action_list = list(actions)
69
self._action_hash = {}
70
71
cdef parent.Parent other
72
for mor in embeddings:
73
other = mor.domain()
74
print "embedding", self, " --> ", other
75
print mor
76
other.init_coerce() # TODO remove when we can
77
other._coerce_from_list.append(mor)
78
79
self._set_element_constructor()
80
81
# old
82
self._has_coerce_map_from = {}
83
if category is not None:
84
self._init_category_(category)
85
86
cdef int init_coerce(self, bint warn=False) except -1:
87
parent.Parent.init_coerce(self, warn)
88
89
90
#################################################################################
91
# New Coercion support functionality
92
#################################################################################
93
94
# def coerce_map_from(self, S):
95
# return self.coerce_map_from_c(S)
96
97
cpdef coerce_map_from_c(self, S):
98
"""
99
EXAMPLES:
100
101
Check to make sure that we handle coerce maps from Python
102
native types correctly::
103
104
sage: QQ['q,t'].coerce_map_from(int)
105
Composite map:
106
From: Set of Python objects of type 'int'
107
To: Multivariate Polynomial Ring in q, t over Rational Field
108
Defn: Native morphism:
109
From: Set of Python objects of type 'int'
110
To: Rational Field
111
then
112
Polynomial base injection morphism:
113
From: Rational Field
114
To: Multivariate Polynomial Ring in q, t over Rational Field
115
"""
116
check_old_coerce(self)
117
if S is self:
118
from sage.categories.homset import Hom
119
return Hom(self, self).identity()
120
elif S == self:
121
# non-unique parents
122
from sage.categories.homset import Hom
123
from sage.categories.morphism import CallMorphism
124
return CallMorphism(Hom(S, self))
125
elif isinstance(S, Set_PythonType_class):
126
return self.coerce_map_from_c(S._type)
127
if self._coerce_from_hash is None: # this is because parent.__init__() does not always get called
128
self.init_coerce()
129
cdef object ret
130
try:
131
ret = PyObject_GetItem(self._coerce_from_hash,S)
132
return ret
133
except KeyError:
134
pass
135
136
if HAS_DICTIONARY(self):
137
mor = self.coerce_map_from_impl(S)
138
else:
139
mor = self.coerce_map_from_c_impl(S)
140
import sage.categories.morphism
141
import sage.categories.map
142
if mor is True:
143
mor = sage.categories.morphism.CallMorphism(S, self)
144
elif mor is False:
145
mor = None
146
elif mor is not None and not isinstance(mor, sage.categories.map.Map):
147
raise TypeError, "coerce_map_from_impl must return a boolean, None, or an explicit Map"
148
149
if mor is None and isinstance(S, type):
150
#Convert Python types to native Sage types
151
sage_type = py_scalar_parent(S)
152
mor = self.coerce_map_from_c(sage_type)
153
if mor is not None:
154
mor = mor * sage_type.coerce_map_from(S)
155
156
if mor is not None:
157
self._coerce_from_hash[S] = mor # TODO: if this is None, could it be non-None in the future?
158
159
return mor
160
161
def coerce_map_from_impl(self, S):
162
check_old_coerce(self)
163
return self.coerce_map_from_c_impl(S)
164
165
cdef coerce_map_from_c_impl(self, S):
166
check_old_coerce(self)
167
import sage.categories.morphism
168
from sage.categories.map import Map
169
from sage.categories.homset import Hom
170
cdef parent.Parent R
171
for mor in self._coerce_from_list:
172
if PY_TYPE_CHECK(mor, Map):
173
R = mor.domain()
174
else:
175
R = mor
176
mor = sage.categories.morphism.CallMorphism(Hom(R, self))
177
i = self._coerce_from_list.index(R)
178
self._coerce_from_list[i] = mor # cache in case we need it again
179
if R is S:
180
return mor
181
else:
182
connecting = R.coerce_map_from(S)
183
if connecting is not None:
184
return mor * connecting
185
186
# Piggyback off the old code for now
187
# WARNING: when working on this, make sure circular dependencies aren't introduced!
188
if self.has_coerce_map_from_c(S):
189
if isinstance(S, type):
190
S = Set_PythonType(S)
191
return sage.categories.morphism.CallMorphism(Hom(S, self))
192
else:
193
return None
194
195
# def get_action(self, S, op=operator.mul, self_on_left=True):
196
# return self.get_action_c(S, op, self_on_left)
197
198
cpdef get_action_c(self, S, op, bint self_on_left):
199
check_old_coerce(self)
200
try:
201
if self._action_hash is None: # this is because parent.__init__() does not always get called
202
self.init_coerce()
203
return self._action_hash[S, op, self_on_left]
204
except KeyError:
205
pass
206
if HAS_DICTIONARY(self):
207
action = self.get_action_impl(S, op, self_on_left)
208
else:
209
action = self.get_action_c_impl(S, op, self_on_left)
210
if action is not None:
211
from sage.categories.action import Action
212
if not isinstance(action, Action):
213
raise TypeError, "get_action_impl must return None or an Action"
214
self._action_hash[S, op, self_on_left] = action
215
return action
216
217
def get_action_impl(self, S, op, self_on_left):
218
check_old_coerce(self)
219
return self.get_action_c_impl(S, op, self_on_left)
220
221
cdef get_action_c_impl(self, S, op, bint self_on_left):
222
check_old_coerce(self)
223
return self.discover_action(S, op, self_on_left)
224
225
#################################################################################
226
# Coercion support functionality
227
#################################################################################
228
229
def _coerce_(self, x): # Call this from Python (do not override!)
230
if self._element_constructor is not None:
231
return self.coerce(x)
232
check_old_coerce(self)
233
return self._coerce_c(x)
234
235
cpdef _coerce_c(self, x): # DO NOT OVERRIDE THIS (call it)
236
if self._element_constructor is not None:
237
return self.coerce(x)
238
check_old_coerce(self)
239
try:
240
P = x.parent() # todo -- optimize
241
if P is self:
242
return x
243
except AttributeError, msg:
244
pass
245
if HAS_DICTIONARY(self):
246
return self._coerce_impl(x)
247
else:
248
return self._coerce_c_impl(x)
249
250
cdef _coerce_c_impl(self, x): # OVERRIDE THIS FOR CYTHON CLASSES
251
"""
252
Canonically coerce x in assuming that the parent of x is not
253
equal to self.
254
"""
255
check_old_coerce(self)
256
raise TypeError
257
258
def _coerce_impl(self, x): # OVERRIDE THIS FOR PYTHON CLASSES
259
"""
260
Canonically coerce x in assuming that the parent of x is not
261
equal to self.
262
"""
263
check_old_coerce(self)
264
return self._coerce_c_impl(x)
265
266
def _coerce_try(self, x, v):
267
"""
268
Given a list v of rings, try to coerce x canonically into each
269
one in turn. Return the __call__ coercion of the result into
270
self of the first canonical coercion that succeeds. Raise a
271
TypeError if none of them succeed.
272
273
INPUT:
274
x -- Python object
275
v -- parent object or list (iterator) of parent objects
276
"""
277
check_old_coerce(self)
278
if not isinstance(v, list):
279
v = [v]
280
281
for R in v:
282
try:
283
y = R._coerce_(x)
284
return self(y)
285
except (TypeError, AttributeError), msg:
286
pass
287
raise TypeError, "no canonical coercion of element into self"
288
289
def _coerce_self(self, x):
290
check_old_coerce(self)
291
return self._coerce_self_c(x)
292
293
cdef _coerce_self_c(self, x):
294
"""
295
Try to canonically coerce x into self.
296
Return result on success or raise TypeError on failure.
297
"""
298
check_old_coerce(self)
299
# todo -- optimize?
300
try:
301
P = x.parent()
302
if P is self:
303
return x
304
elif P == self:
305
return self(x)
306
except AttributeError:
307
pass
308
raise TypeError, "no canonical coercion to self defined"
309
310
# def has_coerce_map_from(self, S):
311
# return self.has_coerce_map_from_c(S)
312
313
cpdef has_coerce_map_from_c(self, S):
314
"""
315
Return True if there is a natural map from S to self.
316
Otherwise, return False.
317
"""
318
check_old_coerce(self)
319
if self == S:
320
return True
321
if self._has_coerce_map_from is None:
322
self._has_coerce_map_from = {}
323
else:
324
try:
325
return self._has_coerce_map_from[S]
326
except KeyError:
327
pass
328
if HAS_DICTIONARY(self):
329
x = self.has_coerce_map_from_impl(S)
330
else:
331
x = self.has_coerce_map_from_c_impl(S)
332
self._has_coerce_map_from[S] = x
333
return x
334
335
def has_coerce_map_from_impl(self, S):
336
check_old_coerce(self)
337
return self.has_coerce_map_from_c_impl(S)
338
339
cdef has_coerce_map_from_c_impl(self, S):
340
check_old_coerce(self)
341
if not PY_TYPE_CHECK(S, parent.Parent):
342
return False
343
try:
344
self._coerce_c((<parent.Parent>S).an_element())
345
except TypeError:
346
return False
347
except NotImplementedError, msg:
348
raise NotImplementedError, "%s\nAlso, please make sure you have implemented has_coerce_map_from_impl or has_coerce_map_from_c_impl (or better _an_element_c_impl or _an_element_impl if possible) for %s"%(msg,self)
349
return True
350
351
def _an_element_impl(self): # override this in Python
352
check_old_coerce(self)
353
return self._an_element_c_impl()
354
355
cdef _an_element_c_impl(self): # override this in Cython
356
"""
357
Returns an element of self. Want it in sufficient generality
358
that poorly-written functions won't work when they're not
359
supposed to. This is cached so doesn't have to be super fast.
360
"""
361
check_old_coerce(self)
362
try:
363
return self.gen(0)
364
except:
365
pass
366
367
try:
368
return self.gen()
369
except:
370
pass
371
372
from sage.rings.infinity import infinity
373
for x in ['_an_element_', 'pi', 1.2, 2, 1, 0, infinity]:
374
try:
375
return self(x)
376
except (TypeError, NameError, NotImplementedError, AttributeError, ValueError):
377
pass
378
379
raise NotImplementedError, "please implement _an_element_c_impl or _an_element_impl for %s"%self
380
381
def _an_element(self): # do not override this (call from Python)
382
check_old_coerce(self)
383
return self._an_element_c()
384
385
cpdef _an_element_c(self): # do not override this (call from Cython)
386
check_old_coerce(self)
387
if not self.__an_element is None:
388
return self.__an_element
389
if HAS_DICTIONARY(self):
390
self.__an_element = self._an_element_impl()
391
else:
392
self.__an_element = self._an_element_c_impl()
393
return self.__an_element
394
395
# This should eventually be inherited from the EnumeratedSets() category
396
# This is just a convenient spot to cover the relevant cython parents,
397
# without bothering the new parents
398
list = parent.Parent._list_from_iterator_cached
399
"""
400
TESTS::
401
402
sage: V = VectorSpace(GF(2,'a'),2)
403
sage: V.list()
404
[(0, 0), (1, 0), (0, 1), (1, 1)]
405
sage: MatrixSpace(GF(3), 1, 1).list()
406
[[0], [1], [2]]
407
sage: DirichletGroup(3).list()
408
[Dirichlet character modulo 3 of conductor 1 mapping 2 |--> 1,
409
Dirichlet character modulo 3 of conductor 3 mapping 2 |--> -1]
410
sage: K = GF(7^6,'a')
411
sage: K.list()[:10] # long time
412
[0, 1, 2, 3, 4, 5, 6, a, a + 1, a + 2]
413
sage: K.<a> = GF(4)
414
sage: K.list()
415
[0, a, a + 1, 1]
416
"""
417
418
419
420
################################################
421
# Comparison of parent objects
422
################################################
423
cdef _richcmp(left, right, int op):
424
"""
425
Compare left and right.
426
"""
427
check_old_coerce(left)
428
cdef int r
429
430
if not PY_TYPE_CHECK(right, parent.Parent) or not PY_TYPE_CHECK(left, parent.Parent):
431
# One is not a parent -- use arbitrary ordering
432
if (<PyObject*>left) < (<PyObject*>right):
433
r = -1
434
elif (<PyObject*>left) > (<PyObject*>right):
435
r = 1
436
else:
437
r = 0
438
439
else:
440
# Both are parents -- but need *not* have the same type.
441
if HAS_DICTIONARY(left):
442
r = left.__cmp__(right)
443
else:
444
r = left._cmp_c_impl(right)
445
446
if op == 0: #<
447
return PyBool_FromLong(r < 0)
448
elif op == 2: #==
449
return PyBool_FromLong(r == 0)
450
elif op == 4: #>
451
return PyBool_FromLong(r > 0)
452
elif op == 1: #<=
453
return PyBool_FromLong(r <= 0)
454
elif op == 3: #!=
455
return PyBool_FromLong(r != 0)
456
elif op == 5: #>=
457
return PyBool_FromLong(r >= 0)
458
459
## ####################################################################
460
## # For a derived Cython class, you **must** put the following in
461
## # your subclasses, in order for it to take advantage of the
462
## # above generic comparison code. You must also define
463
## # _cmp_c_impl for a Cython class.
464
## #
465
## # For a derived Python class, simply define __cmp__.
466
## ####################################################################
467
## def __richcmp__(left, right, int op):
468
## return (<Parent>left)._richcmp(right, op)
469
470
## # NOT NEEDED, since all attributes are public!
471
## def __reduce__(self):
472
## if HAS_DICTIONARY(self):
473
## _dict = self.__dict__
474
## else:
475
## _dict = None
476
## return (make_parent_v0, (self.__class__, _dict, self._has_coerce_map_from))
477
478
cdef int _cmp_c_impl(left, parent.Parent right) except -2:
479
check_old_coerce(left)
480
pass
481
# this would be nice to do, but we can't since
482
# it leads to infinite recursions -- and is slow -- and this
483
# stuff must be fast!
484
#if right.has_coerce_map_from(left):
485
# if left.has_coerce_map_from(right):
486
# return 0
487
# else:
488
# return -1
489
if (<PyObject*>left) < (<PyObject*>right):
490
return -1
491
elif (<PyObject*>left) > (<PyObject*>right):
492
return 1
493
return 0
494
495
## def __cmp__(left, right):
496
## return left._cmp_c_impl(right) # default
497
498
499
############################################################################
500
# Coercion Compatibility Layer
501
############################################################################
502
503
cpdef _coerce_map_from_(self, S):
504
if self._element_constructor is None:
505
return self.coerce_map_from_c(S)
506
else:
507
return parent.Parent._coerce_map_from_(self, S)
508
509
cpdef _get_action_(self, other, op, bint self_on_left):
510
if self._element_constructor is None:
511
return self.get_action_c(other, op, self_on_left)
512
else:
513
return parent.Parent._get_action_(self, other, op, self_on_left)
514
515
def _an_element_(self):
516
if self._element_constructor is None:
517
return self._an_element_c()
518
else:
519
return parent.Parent._an_element_(self)
520
521
cpdef _generic_convert_map(self, S):
522
if self._element_constructor is None:
523
if hasattr(self, '_element_constructor_'):
524
assert callable(self._element_constructor_)
525
self._element_constructor = self._element_constructor_
526
else:
527
from sage.categories.morphism import CallMorphism
528
from sage.categories.homset import Hom
529
if PY_TYPE_CHECK(S, type):
530
S = Set_PythonType(S)
531
return CallMorphism(Hom(S, self))
532
return parent.Parent._generic_convert_map(self, S)
533
534